React/Redux - publish component #323
19
package.json
|
@ -9,7 +9,9 @@
|
||||||
"start": "node speech.js",
|
"start": "node speech.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"fix": "eslint . --fix",
|
"fix": "eslint . --fix",
|
||||||
"precommit": "eslint ."
|
"precommit": "eslint .",
|
||||||
|
"babel": "babel",
|
||||||
|
"webpack": "webpack"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -41,16 +43,27 @@
|
||||||
"nodemon": "^1.11.0",
|
"nodemon": "^1.11.0",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"prop-types": "^15.6.0",
|
||||||
|
"react": "^16.2.0",
|
||||||
|
"react-dom": "^16.2.0",
|
||||||
|
"react-redux": "^5.0.6",
|
||||||
|
"redux": "^3.7.2",
|
||||||
"request": "^2.83.0",
|
"request": "^2.83.0",
|
||||||
"request-promise": "^4.2.2",
|
"request-promise": "^4.2.2",
|
||||||
"sequelize": "^4.1.0",
|
"sequelize": "^4.1.0",
|
||||||
"sequelize-cli": "^3.0.0-3",
|
"sequelize-cli": "^3.0.0-3",
|
||||||
"sleep": "^5.1.1",
|
"sleep": "^5.1.1",
|
||||||
"universal-analytics": "^0.4.13",
|
"universal-analytics": "^0.4.13",
|
||||||
|
"whatwg-fetch": "^2.0.3",
|
||||||
"winston": "^2.3.1",
|
"winston": "^2.3.1",
|
||||||
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"babel-core": "^6.26.0",
|
||||||
|
"babel-loader": "^7.1.2",
|
||||||
|
"babel-preset-es2015": "^6.24.1",
|
||||||
|
"babel-preset-react": "^6.24.1",
|
||||||
|
"babel-preset-stage-2": "^6.24.1",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-http": "^3.0.0",
|
"chai-http": "^3.0.0",
|
||||||
"eslint": "3.19.0",
|
"eslint": "3.19.0",
|
||||||
|
@ -61,6 +74,8 @@
|
||||||
"eslint-plugin-react": "6.10.3",
|
"eslint-plugin-react": "6.10.3",
|
||||||
"eslint-plugin-standard": "3.0.1",
|
"eslint-plugin-standard": "3.0.1",
|
||||||
"husky": "^0.13.4",
|
"husky": "^0.13.4",
|
||||||
"mocha": "^4.0.1"
|
"mocha": "^4.0.1",
|
||||||
|
"redux-devtools": "^3.4.1",
|
||||||
|
"webpack": "^3.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ module.exports = new PassportLocalStrategy(
|
||||||
.then(user => {
|
.then(user => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
// logger.debug('no user found');
|
// logger.debug('no user found');
|
||||||
return done(null, false, {message: 'Incorrect username or password.'});
|
return done(null, false, {message: 'Incorrect username or password'});
|
||||||
}
|
}
|
||||||
user.comparePassword(password, (passwordErr, isMatch) => {
|
user.comparePassword(password, (passwordErr, isMatch) => {
|
||||||
if (passwordErr) {
|
if (passwordErr) {
|
||||||
|
@ -46,7 +46,7 @@ module.exports = new PassportLocalStrategy(
|
||||||
}
|
}
|
||||||
if (!isMatch) {
|
if (!isMatch) {
|
||||||
// logger.debug('incorrect password');
|
// logger.debug('incorrect password');
|
||||||
return done(null, false, {message: 'Incorrect username or password.'});
|
return done(null, false, {message: 'Incorrect username or password'});
|
||||||
}
|
}
|
||||||
logger.debug('Password was a match, returning User');
|
logger.debug('Password was a match, returning User');
|
||||||
return returnUserAndChannelInfo(user)
|
return returnUserAndChannelInfo(user)
|
||||||
|
|
|
@ -483,32 +483,31 @@ table {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#primary-dropzone-instructions, #dropbzone-dragover {
|
#dropzone-text-holder {
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.position-absolute {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
left: 0;
|
||||||
left: 0px;
|
top: 0;
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#asset-preview-holder {
|
#dropzone-dragover, #dropzone-instructions {
|
||||||
position: relative;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#asset-preview {
|
#asset-preview {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0.5rem;
|
width: 100%;
|
||||||
width: calc(100% - 1rem);
|
}
|
||||||
|
|
||||||
|
.dim {
|
||||||
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assets */
|
/* Assets */
|
||||||
|
|
||||||
.asset {
|
.asset {
|
||||||
max-width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#show-body #asset-boilerpate {
|
#show-body #asset-boilerpate {
|
||||||
|
|
BIN
public/assets/img/loading.gif
Normal file
After Width: | Height: | Size: 9 KiB |
|
@ -1,4 +0,0 @@
|
||||||
function sendAuthRequest (channelName, password, url) {
|
|
||||||
const params = `username=${channelName}&password=${password}`;
|
|
||||||
return postRequest(url, params);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
const EMAIL_FORMAT = 'ERROR_EMAIL_FORMAT';
|
|
|
@ -2,13 +2,11 @@
|
||||||
function showChannelCreateInProgressDisplay () {
|
function showChannelCreateInProgressDisplay () {
|
||||||
const publishChannelForm = document.getElementById('publish-channel-form');
|
const publishChannelForm = document.getElementById('publish-channel-form');
|
||||||
const inProgress = document.getElementById('channel-publish-in-progress');
|
const inProgress = document.getElementById('channel-publish-in-progress');
|
||||||
const channelProgressBar = document.getElementById('create-channel-progress-bar');
|
|
||||||
publishChannelForm.hidden = true;
|
publishChannelForm.hidden = true;
|
||||||
inProgress.hidden = false;
|
inProgress.hidden = false;
|
||||||
createProgressBar(channelProgressBar, 12);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// display the content that shows channle creation is done
|
// display the content that shows channel creation is done
|
||||||
function showChannelCreateDoneDisplay() {
|
function showChannelCreateDoneDisplay() {
|
||||||
const inProgress = document.getElementById('channel-publish-in-progress');
|
const inProgress = document.getElementById('channel-publish-in-progress');
|
||||||
inProgress.hidden=true;
|
inProgress.hidden=true;
|
||||||
|
@ -22,35 +20,46 @@ function showChannelCreationError(msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function publishNewChannel (event) {
|
function publishNewChannel (event) {
|
||||||
const userName = document.getElementById('new-channel-name').value;
|
const username = document.getElementById('new-channel-name').value;
|
||||||
const password = document.getElementById('new-channel-password').value;
|
const password = document.getElementById('new-channel-password').value;
|
||||||
// prevent default so this script can handle submission
|
// prevent default so this script can handle submission
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// validate submission
|
// validate submission
|
||||||
validationFunctions.validateNewChannelSubmission(userName, password)
|
validationFunctions.validateNewChannelSubmission(username, password)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
showChannelCreateInProgressDisplay();
|
showChannelCreateInProgressDisplay();
|
||||||
return sendAuthRequest(userName, password, '/signup') // post the request
|
// return sendAuthRequest(userName, password, '/signup') // post the request
|
||||||
|
return fetch('/signup', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({username, password}),
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}),
|
||||||
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then(function(response) {
|
||||||
setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId);
|
if (response.ok){
|
||||||
showChannelCreateDoneDisplay();
|
return response.json();
|
||||||
// if user is on the home page, update the needed elements without reloading
|
|
||||||
if (window.location.pathname === '/') {
|
|
||||||
replaceChannelOptionInPublishChannelSelect(result.channelName);
|
|
||||||
replaceChannelOptionInNavBarChannelSelect(result.channelName);
|
|
||||||
// if user is not on home page, redirect to home page
|
|
||||||
} else {
|
} else {
|
||||||
window.location = '/';
|
throw response;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(signupResult => {
|
||||||
|
console.log('signup success:', signupResult);
|
||||||
|
showChannelCreateDoneDisplay();
|
||||||
|
window.location = '/';
|
||||||
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.name === 'ChannelNameError' || error.name === 'ChannelPasswordError'){
|
if (error.name === 'ChannelNameError' || error.name === 'ChannelPasswordError'){
|
||||||
const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name');
|
const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name');
|
||||||
validationFunctions.showError(channelNameErrorDisplayElement, error.message);
|
validationFunctions.showError(channelNameErrorDisplayElement, error.message);
|
||||||
} else {
|
} else {
|
||||||
console.log('signup failure:', error);
|
console.log('signup failure:', error);
|
||||||
showChannelCreationError('Unfortunately, we encountered an error while creating your channel. Please let us know in slack!');
|
showChannelCreationError('Unfortunately, we encountered an error while creating your channel. Please let us know in slack!', error);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
|
|
||||||
function drop_handler(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
// if dropped items aren't files, reject them
|
|
||||||
var dt = event.dataTransfer;
|
|
||||||
if (dt.items) {
|
|
||||||
if (dt.items[0].kind == 'file') {
|
|
||||||
var droppedFile = dt.items[0].getAsFile();
|
|
||||||
publishFileFunctions.previewAndStageFile(droppedFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragover_handler(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragend_handler(event) {
|
|
||||||
var dt = event.dataTransfer;
|
|
||||||
if (dt.items) {
|
|
||||||
for (var i = 0; i < dt.items.length; i++) {
|
|
||||||
dt.items.remove(i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.dataTransfer.clearData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragenter_handler(event) {
|
|
||||||
var thisDropzone = document.getElementById(event.target.id);
|
|
||||||
thisDropzone.setAttribute('class', 'dropzone dropzone--drag-over row row--margined row--padded row--tall flex-container--column flex-container--center-center');
|
|
||||||
thisDropzone.firstElementChild.setAttribute('class', 'hidden');
|
|
||||||
thisDropzone.lastElementChild.setAttribute('class', '');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragexit_handler(event) {
|
|
||||||
var thisDropzone = document.getElementById(event.target.id);
|
|
||||||
thisDropzone.setAttribute('class', 'dropzone row row--tall row--margined row--padded flex-container--column flex-container--center-center');
|
|
||||||
thisDropzone.firstElementChild.setAttribute('class', '');
|
|
||||||
thisDropzone.lastElementChild.setAttribute('class', 'hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
function preview_onmouseenter_handler () {
|
|
||||||
document.getElementById('asset-preview-dropzone-instructions').setAttribute('class', 'flex-container--column flex-container--center-center position-absolute');
|
|
||||||
document.getElementById('asset-preview').style.opacity = 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
function preview_onmouseleave_handler () {
|
|
||||||
document.getElementById('asset-preview-dropzone-instructions').setAttribute('class', 'hidden');
|
|
||||||
document.getElementById('asset-preview').style.opacity = 1;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,160 +1,3 @@
|
||||||
function getRequest (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 == 401) {
|
|
||||||
reject('Wrong channel name or password');
|
|
||||||
} else {
|
|
||||||
reject('request failed with status:' + xhttp.status);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.send();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function postRequest (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 == 401) {
|
|
||||||
reject( new AuthenticationError('Wrong channel name or password'));
|
|
||||||
} else {
|
|
||||||
reject('request failed with status:' + xhttp.status);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.send(params);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleSection(event){
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
var dataSet = event.target.dataset;
|
|
||||||
var status = dataSet.open;
|
|
||||||
var masterElement = document.getElementById(event.target.id||event.srcElement.id);
|
|
||||||
var slaveElement = document.getElementById(dataSet.slaveelementid);
|
|
||||||
var closedLabel = dataSet.closedlabel;
|
|
||||||
var openLabel = dataSet.openlabel;
|
|
||||||
|
|
||||||
if (status === "false") {
|
|
||||||
slaveElement.hidden = false;
|
|
||||||
masterElement.innerText = openLabel;
|
|
||||||
masterElement.dataset.open = "true";
|
|
||||||
} else {
|
|
||||||
slaveElement.hidden = true;
|
|
||||||
masterElement.innerText = closedLabel;
|
|
||||||
masterElement.dataset.open = "false";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProgressBar(element, size){
|
|
||||||
var x = 0;
|
|
||||||
var adder = 1;
|
|
||||||
// create the bar holder & place it
|
|
||||||
var barHolder = document.createElement('p');
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
const bar = document.createElement('span');
|
|
||||||
bar.innerText = '| ';
|
|
||||||
bar.setAttribute('class', 'progress-bar progress-bar--inactive');
|
|
||||||
barHolder.appendChild(bar);
|
|
||||||
}
|
|
||||||
element.appendChild(barHolder);
|
|
||||||
// get the bars
|
|
||||||
const bars = document.getElementsByClassName('progress-bar');
|
|
||||||
// function to update the bars' classes
|
|
||||||
function updateOneBar(){
|
|
||||||
// update the appropriate bar
|
|
||||||
if (x > -1 && x < size){
|
|
||||||
if (adder === 1){
|
|
||||||
bars[x].setAttribute('class', 'progress-bar progress-bar--active');
|
|
||||||
} else {
|
|
||||||
bars[x].setAttribute('class', 'progress-bar progress-bar--inactive');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// set x
|
|
||||||
if (x === size){
|
|
||||||
adder = -1;
|
|
||||||
} else if ( x === -1){
|
|
||||||
adder = 1;
|
|
||||||
}
|
|
||||||
// update the adder
|
|
||||||
x += adder;
|
|
||||||
|
|
||||||
};
|
|
||||||
// start updater
|
|
||||||
setInterval(updateOneBar, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCookie(key, value) {
|
|
||||||
document.cookie = `${key}=${value}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCookie(cname) {
|
|
||||||
const name = cname + "=";
|
|
||||||
const decodedCookie = decodeURIComponent(document.cookie);
|
|
||||||
const ca = decodedCookie.split(';');
|
|
||||||
for(let i = 0; i <ca.length; i++) {
|
|
||||||
let c = ca[i];
|
|
||||||
while (c.charAt(0) == ' ') {
|
|
||||||
c = c.substring(1);
|
|
||||||
}
|
|
||||||
if (c.indexOf(name) == 0) {
|
|
||||||
return c.substring(name.length, c.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkCookie() {
|
|
||||||
const channelName = getCookie("channel_name");
|
|
||||||
if (channelName != "") {
|
|
||||||
console.log(`cookie found for ${channelName}`);
|
|
||||||
} else {
|
|
||||||
console.log('no channel_name cookie found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearCookie(name) {
|
|
||||||
document.cookie = `${name}=; expires=Thu, 01-Jan-1970 00:00:01 GMT;`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUserCookies(channelName, channelClaimId, shortChannelId) {
|
|
||||||
setCookie('channel_name', channelName)
|
|
||||||
setCookie('channel_claim_id', channelClaimId);
|
|
||||||
setCookie('short_channel_id', shortChannelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearUserCookies() {
|
|
||||||
clearCookie('channel_name')
|
|
||||||
clearCookie('channel_claim_id');
|
|
||||||
clearCookie('short_channel_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyToClipboard(event){
|
|
||||||
var elementToCopy = event.target.dataset.elementtocopy;
|
|
||||||
var element = document.getElementById(elementToCopy);
|
|
||||||
var errorElement = 'input-error-copy-text' + elementToCopy;
|
|
||||||
element.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
} catch (err) {
|
|
||||||
validationFunctions.showError(errorElement, 'Oops, unable to copy');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new error objects, that prototypically inherit from the Error constructor
|
// Create new error objects, that prototypically inherit from the Error constructor
|
||||||
function FileError(message) {
|
function FileError(message) {
|
||||||
this.name = 'FileError';
|
this.name = 'FileError';
|
||||||
|
|
|
@ -1,67 +1,41 @@
|
||||||
function replaceChannelOptionInPublishChannelSelect(loggedInChannel) {
|
|
||||||
// remove the old channel option
|
|
||||||
const oldChannel = document.getElementById('publish-channel-select-channel-option')
|
|
||||||
if (oldChannel){
|
|
||||||
oldChannel.parentNode.removeChild(oldChannel);
|
|
||||||
}
|
|
||||||
// create new channel option
|
|
||||||
const newChannelOption = document.createElement('option');
|
|
||||||
newChannelOption.setAttribute('value', loggedInChannel);
|
|
||||||
newChannelOption.setAttribute('id', 'publish-channel-select-channel-option');
|
|
||||||
newChannelOption.setAttribute('selected', '');
|
|
||||||
newChannelOption.innerText = loggedInChannel;
|
|
||||||
// add the new option
|
|
||||||
const channelSelect = document.getElementById('channel-name-select');
|
|
||||||
channelSelect.insertBefore(newChannelOption, channelSelect.firstChild);
|
|
||||||
// carry out channel selection
|
|
||||||
toggleSelectedChannel(loggedInChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceChannelOptionInNavBarChannelSelect (loggedInChannel) {
|
|
||||||
// remove the old channel option
|
|
||||||
const oldChannel = document.getElementById('nav-bar-channel-select-channel-option');
|
|
||||||
if (oldChannel){
|
|
||||||
oldChannel.parentNode.removeChild(oldChannel);
|
|
||||||
}
|
|
||||||
// create new channel option & select it
|
|
||||||
const newChannelOption = document.createElement('option');
|
|
||||||
newChannelOption.setAttribute('value', loggedInChannel);
|
|
||||||
newChannelOption.setAttribute('id', 'nav-bar-channel-select-channel-option');
|
|
||||||
newChannelOption.setAttribute('selected', '');
|
|
||||||
newChannelOption.innerText = loggedInChannel;
|
|
||||||
// add the new option
|
|
||||||
const channelSelect = document.getElementById('nav-bar-channel-select');
|
|
||||||
channelSelect.style.display = 'inline-block';
|
|
||||||
channelSelect.insertBefore(newChannelOption, channelSelect.firstChild);
|
|
||||||
// hide login
|
|
||||||
const navBarLoginLink = document.getElementById('nav-bar-login-link');
|
|
||||||
navBarLoginLink.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
function loginToChannel (event) {
|
function loginToChannel (event) {
|
||||||
const userName = document.getElementById('channel-login-name-input').value;
|
const username = document.getElementById('channel-login-name-input').value;
|
||||||
const password = document.getElementById('channel-login-password-input').value;
|
const password = document.getElementById('channel-login-password-input').value;
|
||||||
// prevent default
|
// prevent default
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
validationFunctions.validateNewChannelLogin(userName, password)
|
validationFunctions.validateNewChannelLogin(username, password)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return sendAuthRequest(userName, password, '/login')
|
return fetch('/login', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({username, password}),
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}),
|
||||||
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then(function(response) {
|
||||||
setUserCookies(result.channelName, result.channelClaimId, result.shortChannelId);
|
console.log(response);
|
||||||
// if user is on the home page, update the needed elements without reloading
|
if (response.ok){
|
||||||
if (window.location.pathname === '/') {
|
return response.json();
|
||||||
replaceChannelOptionInPublishChannelSelect(result.channelName);
|
|
||||||
replaceChannelOptionInNavBarChannelSelect(result.channelName);
|
|
||||||
// if user is not on home page, redirect to home page
|
|
||||||
} else {
|
} else {
|
||||||
|
throw response;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(function({success, message}) {
|
||||||
|
if (success) {
|
||||||
window.location = '/';
|
window.location = '/';
|
||||||
|
} else {
|
||||||
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
const loginErrorDisplayElement = document.getElementById('login-error-display-element');
|
const loginErrorDisplayElement = document.getElementById('login-error-display-element');
|
||||||
if (error.name){
|
if (error.message){
|
||||||
validationFunctions.showError(loginErrorDisplayElement, error.message);
|
validationFunctions.showError(loginErrorDisplayElement, error.message);
|
||||||
} else {
|
} else {
|
||||||
validationFunctions.showError(loginErrorDisplayElement, 'There was an error logging into your channel');
|
validationFunctions.showError(loginErrorDisplayElement, 'There was an error logging into your channel');
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
function toggleNavBarSelection (value) {
|
|
||||||
const selectedOption = value;
|
|
||||||
if (selectedOption === 'LOGOUT') {
|
|
||||||
// remove session cookies
|
|
||||||
clearUserCookies();
|
|
||||||
// send logout request to server
|
|
||||||
window.location.href = '/logout';
|
|
||||||
} else if (selectedOption === 'VIEW') {
|
|
||||||
// get channel info
|
|
||||||
const channelName = getCookie('channel_name');
|
|
||||||
const channelClaimId = getCookie('channel_claim_id');
|
|
||||||
// redirect to channel page
|
|
||||||
window.location.href = `/${channelName}:${channelClaimId}`;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
var stagedFiles = null;
|
|
||||||
|
|
||||||
const publishFileFunctions = {
|
|
||||||
triggerFileChooser: function (fileInputId) {
|
|
||||||
document.getElementById(fileInputId).click();
|
|
||||||
},
|
|
||||||
cancelPublish: function () {
|
|
||||||
window.location.href = '/';
|
|
||||||
},
|
|
||||||
previewAndStageFile: function (selectedFile) {
|
|
||||||
const fileSelectionInputError = document.getElementById('input-error-file-selection');
|
|
||||||
// When a file is selected for publish, validate that file and
|
|
||||||
// stage it so it will be ready when the publish button is clicked
|
|
||||||
try {
|
|
||||||
validationFunctions.validateFile(selectedFile); // validate the file's name, type, and size
|
|
||||||
} catch (error) {
|
|
||||||
validationFunctions.showError(fileSelectionInputError, error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// set image preview, if an image was provided
|
|
||||||
this.setImagePreview(selectedFile);
|
|
||||||
// hide the primary drop zone
|
|
||||||
this.hidePrimaryDropzone();
|
|
||||||
// set the name input value to the image name if none is set yet
|
|
||||||
this.updateClaimNameInputWithFileName(selectedFile);
|
|
||||||
// store the selected file for upload
|
|
||||||
stagedFiles = [selectedFile];
|
|
||||||
},
|
|
||||||
hidePrimaryDropzone: function () {
|
|
||||||
const primaryDropzone = document.getElementById('primary-dropzone');
|
|
||||||
const publishForm = document.getElementById('publish-form');
|
|
||||||
primaryDropzone.setAttribute('class', 'hidden');
|
|
||||||
publishForm.setAttribute('class', 'row')
|
|
||||||
},
|
|
||||||
updateClaimNameInputWithFileName: function (selectedFile) {
|
|
||||||
const nameInput = document.getElementById('claim-name-input');
|
|
||||||
if (nameInput.value === "") {
|
|
||||||
var filename = selectedFile.name.substring(0, selectedFile.name.indexOf('.'))
|
|
||||||
nameInput.value = validationFunctions.cleanseClaimName(filename);
|
|
||||||
validationFunctions.checkClaimName(nameInput.value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setImagePreview: function (selectedFile) {
|
|
||||||
const assetPreview = document.getElementById('asset-preview-target');
|
|
||||||
const thumbnailInput = document.getElementById('claim-thumbnail-input');
|
|
||||||
const thumbnailInputTool = document.getElementById('publish-thumbnail');
|
|
||||||
if (selectedFile.type !== 'video/mp4') {
|
|
||||||
const previewReader = new FileReader();
|
|
||||||
if (selectedFile.type === 'image/gif') {
|
|
||||||
assetPreview.innerHTML = `<p>loading preview...</p>`
|
|
||||||
}
|
|
||||||
previewReader.readAsDataURL(selectedFile);
|
|
||||||
previewReader.onloadend = function () {
|
|
||||||
assetPreview.innerHTML = '<img id="asset-preview" src="' + previewReader.result + '" alt="image preview"/>';
|
|
||||||
};
|
|
||||||
// clear & hide the thumbnail selection input
|
|
||||||
thumbnailInput.value = '';
|
|
||||||
thumbnailInputTool.hidden = true;
|
|
||||||
} else {
|
|
||||||
assetPreview.innerHTML = `<img id="asset-preview" src="/assets/img/video_thumb_default.png"/>`;
|
|
||||||
// clear & show the thumbnail selection input
|
|
||||||
thumbnailInput.value = '';
|
|
||||||
thumbnailInputTool.hidden = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
returnNullOrChannel: function () {
|
|
||||||
const channelRadio = document.getElementById('channel-radio');
|
|
||||||
if (channelRadio.checked) {
|
|
||||||
const channelInput = document.getElementById('channel-name-select');
|
|
||||||
return channelInput.value.trim();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
createMetadata: function() {
|
|
||||||
const nameInput = document.getElementById('claim-name-input');
|
|
||||||
const titleInput = document.getElementById('publish-title');
|
|
||||||
const descriptionInput = document.getElementById('publish-description');
|
|
||||||
const licenseInput = document.getElementById('publish-license');
|
|
||||||
const nsfwInput = document.getElementById('publish-nsfw');
|
|
||||||
const thumbnailInput = document.getElementById('claim-thumbnail-input');
|
|
||||||
const channelName = this.returnNullOrChannel();
|
|
||||||
let metadata = {
|
|
||||||
name: nameInput.value.trim(),
|
|
||||||
title: titleInput.value.trim(),
|
|
||||||
description: descriptionInput.value.trim(),
|
|
||||||
license: licenseInput.value.trim(),
|
|
||||||
nsfw: nsfwInput.checked,
|
|
||||||
type: stagedFiles[0].type,
|
|
||||||
thumbnail: thumbnailInput.value.trim(),
|
|
||||||
};
|
|
||||||
if (channelName) {
|
|
||||||
metadata['channelName'] = channelName;
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
},
|
|
||||||
appendDataToFormData: function (file, metadata) {
|
|
||||||
var fd = new FormData();
|
|
||||||
fd.append('file', file)
|
|
||||||
for (var key in metadata) {
|
|
||||||
if (metadata.hasOwnProperty(key)) {
|
|
||||||
console.log(key, metadata[key]);
|
|
||||||
fd.append(key, metadata[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
},
|
|
||||||
publishFile: function (file, metadata) {
|
|
||||||
var uri = "/api/claim-publish";
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
var fd = this.appendDataToFormData(file, metadata);
|
|
||||||
var that = this;
|
|
||||||
xhr.upload.addEventListener("loadstart", function(e) {
|
|
||||||
that.showUploadStartedMessage();
|
|
||||||
})
|
|
||||||
xhr.upload.addEventListener("progress", function(e) {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
var percentage = Math.round((e.loaded * 100) / e.total);
|
|
||||||
console.log('progress:', percentage);
|
|
||||||
that.showUploadProgressMessage(percentage);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
xhr.upload.addEventListener("load", function(e){
|
|
||||||
console.log('loaded 100%');
|
|
||||||
that.showFilePublishUpdate("Your file has been loaded, and is now being published to the blockchain. Sit tight...")
|
|
||||||
}, false);
|
|
||||||
xhr.open("POST", uri, true);
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState == 4) {
|
|
||||||
console.log('publish response:', xhr.response)
|
|
||||||
if (xhr.status == 200) {
|
|
||||||
console.log('publish complete!');
|
|
||||||
that.showFilePublishComplete(JSON.parse(xhr.response).message);
|
|
||||||
} else if (xhr.status == 502){
|
|
||||||
that.showFilePublishFailure('Spee.ch was not able to get a response from the LBRY network.');
|
|
||||||
} else {
|
|
||||||
that.showFilePublishFailure(JSON.parse(xhr.response).message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Initiate a multipart/form-data upload
|
|
||||||
xhr.send(fd);
|
|
||||||
},
|
|
||||||
// Validate the publish submission and then trigger upload
|
|
||||||
publishStagedFile: function (event) {
|
|
||||||
event.preventDefault(); // prevent default so this script can handle submission
|
|
||||||
const metadata = this.createMetadata();
|
|
||||||
const that = this;
|
|
||||||
const fileSelectionInputError = document.getElementById('input-error-file-selection');
|
|
||||||
const claimNameError = document.getElementById('input-error-claim-name');
|
|
||||||
const channelSelectError = document.getElementById('input-error-channel-select');
|
|
||||||
const publishSubmitError = document.getElementById('input-error-publish-submit');
|
|
||||||
// validate, submit, and handle response
|
|
||||||
validationFunctions.validateFilePublishSubmission(stagedFiles, metadata)
|
|
||||||
.then( function() {
|
|
||||||
that.publishFile(stagedFiles[0], metadata);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
if (error.name === 'FileError') {
|
|
||||||
validationFunctions.showError(fileSelectionInputError, error.message);
|
|
||||||
} else if (error.name === 'NameError') {
|
|
||||||
validationFunctions.showError(claimNameError, error.message);
|
|
||||||
} else if (error.name === 'ChannelNameError'){
|
|
||||||
console.log(error);
|
|
||||||
validationFunctions.showError(channelSelectError, error.message);
|
|
||||||
} else {
|
|
||||||
validationFunctions.showError(publishSubmitError, error.message);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
},
|
|
||||||
showUploadStartedMessage: function (){
|
|
||||||
console.log('starting upload');
|
|
||||||
// hide the publish tool
|
|
||||||
this.hidePublishTools();
|
|
||||||
// show the progress status and animation
|
|
||||||
this.showPublishStatus();
|
|
||||||
this.showPublishProgressBar();
|
|
||||||
},
|
|
||||||
showUploadProgressMessage: function (percentage){
|
|
||||||
this.updatePublishStatus('<p>File is loading to server</p>');
|
|
||||||
this.updateUploadPercent('<p class="blue">' + percentage + '% </p>');
|
|
||||||
},
|
|
||||||
showFilePublishUpdate: function (msg) {
|
|
||||||
this.updatePublishStatus('<p>' + msg + '</p>');
|
|
||||||
this.updateUploadPercent('<p>Curious what magic is happening here? <a class="link--primary" target="blank" href="https://lbry.io/faq/what-is-lbry">Learn more.</a></p>');
|
|
||||||
},
|
|
||||||
showFilePublishFailure: function (msg){
|
|
||||||
this.updatePublishStatus('<p>Something went wrong...</p><p><strong>' + msg + '</strong></p><p>For help, post the above error text in the #speech channel on the <a class="link--primary" href="https://discord.gg/YjYbwhS" target="_blank">lbry discord</a>');
|
|
||||||
this.hidePublishProgressBar();
|
|
||||||
this.hideUploadPercent();
|
|
||||||
},
|
|
||||||
showFilePublishComplete: function (msg) {
|
|
||||||
console.log('Publish complete!');
|
|
||||||
const showUrl = msg.lbryTx.claim_id + "/" + msg.name;
|
|
||||||
// update status
|
|
||||||
this.updatePublishStatus('<p>Your publish is complete! You are being redirected to it now.</p>');
|
|
||||||
this.updateUploadPercent('<p><a class="link--primary" target="_blank" href="' + showUrl + '">If you do not get redirected, click here.</a></p>')
|
|
||||||
// redirect the user
|
|
||||||
window.location.href = showUrl;
|
|
||||||
},
|
|
||||||
hidePublishTools: function () {
|
|
||||||
const publishFormWrapper = document.getElementById('publish-form');
|
|
||||||
publishFormWrapper.setAttribute('class', 'hidden');
|
|
||||||
},
|
|
||||||
// publish status functions
|
|
||||||
showPublishStatus: function () {
|
|
||||||
const publishStatus = document.getElementById('publish-status');
|
|
||||||
publishStatus.setAttribute('class', 'row row--tall flex-container--column flex-container--center-center');
|
|
||||||
},
|
|
||||||
updatePublishStatus: function (msg){
|
|
||||||
const publishUpdate = document.getElementById('publish-update');
|
|
||||||
publishUpdate.innerHTML = msg;
|
|
||||||
},
|
|
||||||
// progress bar functions
|
|
||||||
showPublishProgressBar: function (){
|
|
||||||
const publishProgressBar = document.getElementById('publish-progress-bar');
|
|
||||||
createProgressBar(publishProgressBar, 12);
|
|
||||||
},
|
|
||||||
hidePublishProgressBar: function (){
|
|
||||||
const publishProgressBar = document.getElementById('publish-progress-bar');
|
|
||||||
publishProgressBar.hidden = true;
|
|
||||||
},
|
|
||||||
// upload percent functions
|
|
||||||
updateUploadPercent: function (msg){
|
|
||||||
const uploadPercent = document.getElementById('upload-percent');
|
|
||||||
uploadPercent.innerHTML = msg;
|
|
||||||
},
|
|
||||||
hideUploadPercent: function (){
|
|
||||||
const uploadPercent = document.getElementById('upload-percent');
|
|
||||||
uploadPercent.hidden = true;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,53 +1,5 @@
|
||||||
// validation function which checks the proposed file's type, size, and name
|
// validation function which checks the proposed file's type, size, and name
|
||||||
const validationFunctions = {
|
const validationFunctions = {
|
||||||
validateFile: function (file) {
|
|
||||||
if (!file) {
|
|
||||||
console.log('no file found');
|
|
||||||
throw new Error('no file provided');
|
|
||||||
}
|
|
||||||
if (/'/.test(file.name)) {
|
|
||||||
console.log('file name had apostrophe in it');
|
|
||||||
throw new Error('apostrophes are not allowed in the file name');
|
|
||||||
}
|
|
||||||
// validate size and type
|
|
||||||
switch (file.type) {
|
|
||||||
case 'image/jpeg':
|
|
||||||
case 'image/jpg':
|
|
||||||
case 'image/png':
|
|
||||||
if (file.size > 10000000) {
|
|
||||||
console.log('file was too big');
|
|
||||||
throw new Error('Sorry, images are limited to 10 megabytes.');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'image/gif':
|
|
||||||
if (file.size > 50000000) {
|
|
||||||
console.log('file was too big');
|
|
||||||
throw new Error('Sorry, .gifs are limited to 50 megabytes.');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'video/mp4':
|
|
||||||
if (file.size > 50000000) {
|
|
||||||
console.log('file was too big');
|
|
||||||
throw new Error('Sorry, videos are limited to 50 megabytes.');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log('file type is not supported');
|
|
||||||
throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// validation function that checks to make sure the claim name is valid
|
|
||||||
validateClaimName: function (name) {
|
|
||||||
// ensure a name was entered
|
|
||||||
if (name.length < 1) {
|
|
||||||
throw new NameError("You must enter a name for your url");
|
|
||||||
}
|
|
||||||
// validate the characters in the 'name' field
|
|
||||||
const invalidCharacters = /[^A-Za-z0-9,-]/g.exec(name);
|
|
||||||
if (invalidCharacters) {
|
|
||||||
throw new NameError('"' + invalidCharacters + '" characters are not allowed');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
validateChannelName: function (name) {
|
validateChannelName: function (name) {
|
||||||
name = name.substring(name.indexOf('@') + 1);
|
name = name.substring(name.indexOf('@') + 1);
|
||||||
// ensure a name was entered
|
// ensure a name was entered
|
||||||
|
@ -65,15 +17,21 @@ const validationFunctions = {
|
||||||
throw new ChannelPasswordError("You must enter a password for you channel");
|
throw new ChannelPasswordError("You must enter a password for you channel");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cleanseClaimName: function (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;
|
|
||||||
},
|
|
||||||
// validation functions to check claim & channel name eligibility as the inputs change
|
// validation functions to check claim & channel name eligibility as the inputs change
|
||||||
|
isChannelNameAvailable: function (name) {
|
||||||
|
return this.isNameAvailable(name, '/api/channel-is-available/');
|
||||||
|
},
|
||||||
isNameAvailable: function (name, apiUrl) {
|
isNameAvailable: function (name, apiUrl) {
|
||||||
|
console.log('isNameAvailable?', name);
|
||||||
const url = apiUrl + name;
|
const url = apiUrl + name;
|
||||||
return getRequest(url)
|
return fetch(url)
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('isNameAvailable error', error);
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
},
|
},
|
||||||
showError: function (errorDisplay, errorMsg) {
|
showError: function (errorDisplay, errorMsg) {
|
||||||
errorDisplay.hidden = false;
|
errorDisplay.hidden = false;
|
||||||
|
@ -91,20 +49,24 @@ const validationFunctions = {
|
||||||
successElement.hidden = true;
|
successElement.hidden = true;
|
||||||
successElement.innerHTML = "";
|
successElement.innerHTML = "";
|
||||||
},
|
},
|
||||||
checkAvailability: function (name, successDisplayElement, errorDisplayElement, validateName, errorMessage, apiUrl) {
|
checkChannelName: function (name) {
|
||||||
|
var successDisplayElement = document.getElementById('input-success-channel-name');
|
||||||
|
var errorDisplayElement = document.getElementById('input-error-channel-name');
|
||||||
|
var channelName = `@${name}`;
|
||||||
var that = this;
|
var that = this;
|
||||||
try {
|
try {
|
||||||
// check to make sure the characters are valid
|
// check to make sure the characters are valid
|
||||||
validateName(name);
|
that.validateChannelName(channelName);
|
||||||
// check to make sure it is available
|
// check to make sure it is available
|
||||||
that.isNameAvailable(name, apiUrl)
|
that.isChannelNameAvailable(channelName)
|
||||||
.then(function (result) {
|
.then(function(isAvailable){
|
||||||
if (result === true) {
|
console.log('isChannelNameAvailable:', isAvailable);
|
||||||
|
if (isAvailable) {
|
||||||
that.hideError(errorDisplayElement);
|
that.hideError(errorDisplayElement);
|
||||||
that.showSuccess(successDisplayElement)
|
that.showSuccess(successDisplayElement)
|
||||||
} else {
|
} else {
|
||||||
that.hideSuccess(successDisplayElement);
|
that.hideSuccess(successDisplayElement);
|
||||||
that.showError(errorDisplayElement, errorMessage);
|
that.showError(errorDisplayElement, 'Sorry, that name is already taken');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -116,65 +78,6 @@ const validationFunctions = {
|
||||||
that.showError(errorDisplayElement, error.message);
|
that.showError(errorDisplayElement, error.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkClaimName: function (name) {
|
|
||||||
const successDisplayElement = document.getElementById('input-success-claim-name');
|
|
||||||
const errorDisplayElement = document.getElementById('input-error-claim-name');
|
|
||||||
this.checkAvailability(name, successDisplayElement, errorDisplayElement, this.validateClaimName, 'Sorry, that ending is already taken', '/api/claim-is-available/');
|
|
||||||
},
|
|
||||||
checkChannelName: function (name) {
|
|
||||||
const successDisplayElement = document.getElementById('input-success-channel-name');
|
|
||||||
const errorDisplayElement = document.getElementById('input-error-channel-name');
|
|
||||||
name = `@${name}`;
|
|
||||||
this.checkAvailability(name, successDisplayElement, errorDisplayElement, this.validateChannelName, 'Sorry, that name is already taken', '/api/channel-is-available/');
|
|
||||||
},
|
|
||||||
// validation function which checks all aspects of the publish submission
|
|
||||||
validateFilePublishSubmission: function (stagedFiles, metadata) {
|
|
||||||
const channelName = metadata.channelName;
|
|
||||||
const claimName = metadata.name;
|
|
||||||
var that = this;
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
// 1. make sure 1 file was staged
|
|
||||||
if (!stagedFiles) {
|
|
||||||
reject(new FileError("Please select a file"));
|
|
||||||
return;
|
|
||||||
} else if (stagedFiles.length > 1) {
|
|
||||||
reject(new FileError("Only one file is allowed at a time"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 2. validate the file's name, type, and size
|
|
||||||
try {
|
|
||||||
that.validateFile(stagedFiles[0]);
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 3. validate that a channel was chosen
|
|
||||||
if (channelName === 'new' || channelName === 'login') {
|
|
||||||
reject(new ChannelNameError("Please log in to a channel"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
// 4. validate the claim name
|
|
||||||
try {
|
|
||||||
that.validateClaimName(claimName);
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if all validation passes, check availability of the name (note: do we need to re-validate channel name vs. credentials as well?)
|
|
||||||
return that.isNameAvailable(claimName, '/api/claim-is-available/')
|
|
||||||
.then(result => {
|
|
||||||
if (result) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(new NameError('Sorry, that ending is already taken'));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// validation function which checks all aspects of a new channel submission
|
// validation function which checks all aspects of a new channel submission
|
||||||
validateNewChannelSubmission: function (userName, password) {
|
validateNewChannelSubmission: function (userName, password) {
|
||||||
const channelName = `@${userName}`;
|
const channelName = `@${userName}`;
|
||||||
|
@ -193,16 +96,15 @@ const validationFunctions = {
|
||||||
return reject(error);
|
return reject(error);
|
||||||
}
|
}
|
||||||
// 3. if all validation passes, check availability of the name
|
// 3. if all validation passes, check availability of the name
|
||||||
that.isNameAvailable(channelName, '/api/channel-is-available/') // validate the availability
|
that.isChannelNameAvailable(channelName)
|
||||||
.then(function(result) {
|
.then(function(isAvailable) {
|
||||||
if (result) {
|
if (isAvailable) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
reject(new ChannelNameError('Sorry, that name is already taken'));
|
reject(new ChannelNameError('Sorry, that name is already taken'));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
console.log('error evaluating channel name availability', error);
|
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
19012
public/bundle/bundle.js
Normal file
12
react/actions/channel.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import * as actions from 'constants/channel_action_types';
|
||||||
|
|
||||||
|
// export action creators
|
||||||
|
|
||||||
|
export function updateLoggedInChannel (name, shortId, longId) {
|
||||||
|
return {
|
||||||
|
type: actions.CHANNEL_UPDATE,
|
||||||
|
name,
|
||||||
|
shortId,
|
||||||
|
longId,
|
||||||
|
};
|
||||||
|
};
|
67
react/actions/publish.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import * as actions from 'constants/publish_action_types';
|
||||||
|
|
||||||
|
// export action creators
|
||||||
|
export function selectFile (file) {
|
||||||
|
return {
|
||||||
|
type: actions.FILE_SELECTED,
|
||||||
|
file: file,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function clearFile () {
|
||||||
|
return {
|
||||||
|
type: actions.FILE_CLEAR,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updateMetadata (name, value) {
|
||||||
|
return {
|
||||||
|
type: actions.METADATA_UPDATE,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updateClaim (value) {
|
||||||
|
return {
|
||||||
|
type: actions.CLAIM_UPDATE,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setPublishInChannel (channel) {
|
||||||
|
return {
|
||||||
|
type: actions.SET_PUBLISH_IN_CHANNEL,
|
||||||
|
channel,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updatePublishStatus (status, message) {
|
||||||
|
return {
|
||||||
|
type: actions.PUBLISH_STATUS_UPDATE,
|
||||||
|
status,
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updateError (name, value) {
|
||||||
|
return {
|
||||||
|
type: actions.ERROR_UPDATE,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updateSelectedChannel (value) {
|
||||||
|
return {
|
||||||
|
type: actions.SELECTED_CHANNEL_UPDATE,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function toggleMetadataInputs (value) {
|
||||||
|
return {
|
||||||
|
type: actions.TOGGLE_METADATA_INPUTS,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
26
react/app.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import {Provider} from 'react-redux';
|
||||||
|
import {createStore} from 'redux';
|
||||||
|
import Reducer from 'reducers';
|
||||||
|
import Publish from 'containers/PublishTool';
|
||||||
|
import NavBar from 'containers/NavBar';
|
||||||
|
|
||||||
|
let store = createStore(
|
||||||
|
Reducer,
|
||||||
|
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<NavBar />
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('react-nav-bar')
|
||||||
|
)
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<Publish />
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('react-publish-tool')
|
||||||
|
)
|
7
react/components/ActiveStatusBar/index.jsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ActiveStatusBar = () => {
|
||||||
|
return <span className="progress-bar progress-bar--active">| </span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActiveStatusBar;
|
36
react/components/ExpandingTextArea/index.jsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
class ExpandingTextarea extends Component {
|
||||||
|
componentDidMount () {
|
||||||
|
this.adjustTextarea({});
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { onChange, ...rest } = this.props;
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
{ ...rest }
|
||||||
|
ref={x => this.el = x}
|
||||||
|
onChange={ this._handleChange.bind(this) }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleChange (event) {
|
||||||
|
const { onChange } = this.props;
|
||||||
|
if (onChange) onChange(event);
|
||||||
|
this.adjustTextarea(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustTextarea ({ target = this.el }) {
|
||||||
|
target.style.height = 0;
|
||||||
|
target.style.height = `${target.scrollHeight}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpandingTextarea.propTypes = {
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExpandingTextarea;
|
7
react/components/InactiveStatusBar/index.jsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const InactiveStatusBar = () => {
|
||||||
|
return <span className="progress-bar progress-bar--inactive">| </span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InactiveStatusBar;
|
28
react/components/Logo/index.jsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Logo () {
|
||||||
|
return (
|
||||||
|
<svg version="1.1" id="Layer_1" x="0px" y="0px" height="24px" viewBox="0 0 80 31" enableBackground="new 0 0 80 31" className="nav-bar-logo">
|
||||||
|
<a href="/">
|
||||||
|
<title>Logo</title>
|
||||||
|
<desc>Spee.ch logo</desc>
|
||||||
|
<g id="About">
|
||||||
|
<g id="Publish-Form-V2-_x28_filled_x29_" transform="translate(-42.000000, -23.000000)">
|
||||||
|
<g id="Group-17" transform="translate(42.000000, 22.000000)">
|
||||||
|
<text transform="matrix(1 0 0 1 0 20)" fontSize="25" fontFamily="Roboto">Spee<h</text>
|
||||||
|
<g id="Group-16" transform="translate(0.000000, 30.000000)">
|
||||||
|
<path id="Line-8" fill="none" stroke="#09F911" strokeWidth="1" strokeLinecap="square" d="M0.5,1.5h15"/>
|
||||||
|
<path id="Line-8-Copy" fill="none" stroke="#029D74" strokeWidth="1" strokeLinecap="square" d="M16.5,1.5h15"/>
|
||||||
|
<path id="Line-8-Copy-2" fill="none" stroke="#E35BD8" strokeWidth="1" strokeLinecap="square" d="M32.5,1.5h15"/>
|
||||||
|
<path id="Line-8-Copy-3" fill="none" stroke="#4156C5" strokeWidth="1" strokeLinecap="square" d="M48.5,1.5h15"/>
|
||||||
|
<path id="Line-8-Copy-4" fill="none" stroke="#635688" strokeWidth="1" strokeLinecap="square" d="M64.5,1.5h15"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</a>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Logo;
|
13
react/components/NavBarChannelOptionsDropdown/index.jsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function NavBarChannelOptionsDropdown ({ channelName, handleSelection, VIEW, LOGOUT }) {
|
||||||
|
return (
|
||||||
|
<select type="text" id="nav-bar-channel-select" className="select select--arrow link--nav" onChange={handleSelection}>
|
||||||
|
<option id="nav-bar-channel-select-channel-option">{channelName}</option>
|
||||||
|
<option value={VIEW}>View</option>
|
||||||
|
<option value={LOGOUT}>Logout</option>
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavBarChannelOptionsDropdown;
|
54
react/components/Preview/index.jsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
class Preview extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
imgSource : '',
|
||||||
|
defaultThumbnail: '/assets/img/video_thumb_default.png',
|
||||||
|
};
|
||||||
|
this.previewFile = this.previewFile.bind(this);
|
||||||
|
}
|
||||||
|
componentDidMount () {
|
||||||
|
this.previewFile(this.props.file);
|
||||||
|
}
|
||||||
|
componentWillReceiveProps (newProps) {
|
||||||
|
if (newProps.file !== this.props.file) {
|
||||||
|
this.previewFile(newProps.file);
|
||||||
|
}
|
||||||
|
if (newProps.thumbnail !== this.props.thumbnail) {
|
||||||
|
this.setState({imgSource: (newProps.thumbnail || this.state.defaultThumbnail)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previewFile (file) {
|
||||||
|
const that = this;
|
||||||
|
if (file.type !== 'video/mp4') {
|
||||||
|
const previewReader = new FileReader();
|
||||||
|
previewReader.readAsDataURL(file);
|
||||||
|
previewReader.onloadend = function () {
|
||||||
|
that.setState({imgSource: previewReader.result});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
that.setState({imgSource: (this.props.thumbnail || this.state.defaultThumbnail)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
id="asset-preview"
|
||||||
|
src={this.state.imgSource}
|
||||||
|
className={this.props.dimPreview ? 'dim' : ''}
|
||||||
|
alt="publish preview"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Preview.propTypes = {
|
||||||
|
dimPreview: PropTypes.bool.isRequired,
|
||||||
|
file : PropTypes.object.isRequired,
|
||||||
|
thumbnail : PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Preview;
|
76
react/components/ProgressBar/index.jsx
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ActiveStatusBar from 'components/ActiveStatusBar';
|
||||||
|
import InactiveStatusBar from 'components/InactiveStatusBar';
|
||||||
|
|
||||||
|
class ProgressBar extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
bars : [],
|
||||||
|
index : 0,
|
||||||
|
incrementer: 1,
|
||||||
|
};
|
||||||
|
this.createBars = this.createBars.bind(this);
|
||||||
|
this.startProgressBar = this.startProgressBar.bind(this);
|
||||||
|
this.updateProgressBar = this.updateProgressBar.bind(this);
|
||||||
|
this.stopProgressBar = this.stopProgressBar.bind(this);
|
||||||
|
}
|
||||||
|
componentDidMount () {
|
||||||
|
this.createBars();
|
||||||
|
this.startProgressBar();
|
||||||
|
}
|
||||||
|
componentWillUnmount () {
|
||||||
|
this.stopProgressBar();
|
||||||
|
}
|
||||||
|
createBars () {
|
||||||
|
const bars = [];
|
||||||
|
for (let i = 0; i <= this.props.size; i++) {
|
||||||
|
bars.push({isActive: false});
|
||||||
|
}
|
||||||
|
this.setState({ bars });
|
||||||
|
}
|
||||||
|
startProgressBar () {
|
||||||
|
this.updateInterval = setInterval(this.updateProgressBar.bind(this), 300);
|
||||||
|
};
|
||||||
|
updateProgressBar () {
|
||||||
|
let index = this.state.index;
|
||||||
|
let incrementer = this.state.incrementer;
|
||||||
|
let bars = this.state.bars;
|
||||||
|
// flip incrementer if necessary, to stay in bounds
|
||||||
|
if ((index < 0) || (index > this.props.size)) {
|
||||||
|
incrementer = incrementer * -1;
|
||||||
|
index += incrementer;
|
||||||
|
}
|
||||||
|
// update the indexed bar
|
||||||
|
if (incrementer > 0) {
|
||||||
|
bars[index].isActive = true;
|
||||||
|
} else {
|
||||||
|
bars[index].isActive = false;
|
||||||
|
};
|
||||||
|
// increment index
|
||||||
|
index += incrementer;
|
||||||
|
// update state
|
||||||
|
this.setState({
|
||||||
|
bars,
|
||||||
|
incrementer,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
stopProgressBar () {
|
||||||
|
clearInterval(this.updateInterval);
|
||||||
|
};
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.state.bars.map((bar, index) => bar.isActive ? <ActiveStatusBar key={index} /> : <InactiveStatusBar key={index}/>)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressBar.propTypes = {
|
||||||
|
size: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProgressBar;
|
52
react/components/PublishStatus/index.jsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ProgressBar from 'components/ProgressBar';
|
||||||
|
import * as publishStates from 'constants/publish_claim_states';
|
||||||
|
|
||||||
|
function PublishStatus ({ status, message }) {
|
||||||
|
return (
|
||||||
|
<div className="row row--tall flex-container--column flex-container--center-center">
|
||||||
|
{(status === publishStates.LOAD_START) &&
|
||||||
|
<div className="row align-content-center">
|
||||||
|
<p>File is loading to server</p>
|
||||||
|
<p className="blue">{message}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{(status === publishStates.LOADING) &&
|
||||||
|
<div>
|
||||||
|
<div className="row align-content-center">
|
||||||
|
<p>File is loading to server</p>
|
||||||
|
<p className="blue">{message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{(status === publishStates.PUBLISHING) &&
|
||||||
|
<div className="row align-content-center">
|
||||||
|
<p>Upload complete. Your file is now being published on the blockchain...</p>
|
||||||
|
<ProgressBar size={12}/>
|
||||||
|
<p>Curious what magic is happening here? <a className="link--primary" target="blank" href="https://lbry.io/faq/what-is-lbry">Learn more.</a></p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{(status === publishStates.SUCCESS) &&
|
||||||
|
<div className="row align-content-center">
|
||||||
|
<p>Your publish is complete! You are being redirected to it now.</p>
|
||||||
|
<p>If you are not automatically redirected, <a class="link--primary" target="_blank" href={message}>click here.</a></p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{(status === publishStates.FAILED) &&
|
||||||
|
<div className="row align-content-center">
|
||||||
|
<p>Something went wrong...</p>
|
||||||
|
<p><strong>{message}</strong></p>
|
||||||
|
<p>For help, post the above error text in the #speech channel on the <a className="link--primary" href="https://discord.gg/YjYbwhS" target="_blank">lbry discord</a></p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PublishStatus.propTypes = {
|
||||||
|
status : PropTypes.string.isRequired,
|
||||||
|
message: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublishStatus;
|
23
react/components/PublishUrlMiddleDisplay/index.jsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
function UrlMiddle ({publishInChannel, selectedChannel, loggedInChannelName, loggedInChannelShortId}) {
|
||||||
|
if (publishInChannel) {
|
||||||
|
if (selectedChannel === 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UrlMiddle.propTypes = {
|
||||||
|
publishInChannel : PropTypes.bool.isRequired,
|
||||||
|
loggedInChannelName : PropTypes.string,
|
||||||
|
loggedInChannelShortId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UrlMiddle;
|
1
react/constants/channel_action_types.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const CHANNEL_UPDATE = 'CHANNEL_UPDATE';
|
9
react/constants/publish_action_types.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export const FILE_SELECTED = 'FILE_SELECTED';
|
||||||
|
export const FILE_CLEAR = 'FILE_CLEAR';
|
||||||
|
export const METADATA_UPDATE = 'METADATA_UPDATE';
|
||||||
|
export const CLAIM_UPDATE = 'CLAIM_UPDATE';
|
||||||
|
export const SET_PUBLISH_IN_CHANNEL = 'SET_PUBLISH_IN_CHANNEL';
|
||||||
|
export const PUBLISH_STATUS_UPDATE = 'PUBLISH_STATUS_UPDATE';
|
||||||
|
export const ERROR_UPDATE = 'ERROR_UPDATE';
|
||||||
|
export const SELECTED_CHANNEL_UPDATE = 'SELECTED_CHANNEL_UPDATE';
|
||||||
|
export const TOGGLE_METADATA_INPUTS = 'TOGGLE_METADATA_INPUTS';
|
2
react/constants/publish_channel_select_states.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export const LOGIN = 'Existing';
|
||||||
|
export const CREATE = 'New';
|
5
react/constants/publish_claim_states.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export const LOAD_START = 'LOAD_START';
|
||||||
|
export const LOADING = 'LOADING';
|
||||||
|
export const PUBLISHING = 'PUBLISHING';
|
||||||
|
export const SUCCESS = 'SUCCESS';
|
||||||
|
export const FAILED = 'FAILED';
|
15
react/containers/ChannelCreateForm/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { updateLoggedInChannel } from 'actions/channel';
|
||||||
|
import View from './view';
|
||||||
|
import {updateSelectedChannel} from '../../actions/publish';
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onChannelLogin: (name, shortId, longId) => {
|
||||||
|
dispatch(updateLoggedInChannel(name, shortId, longId));
|
||||||
|
dispatch(updateSelectedChannel(name));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(View);
|
168
react/containers/ChannelCreateForm/view.jsx
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ProgressBar from 'components/ProgressBar';
|
||||||
|
import request from 'utils/request';
|
||||||
|
|
||||||
|
class ChannelCreateForm extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
error : null,
|
||||||
|
channel : '',
|
||||||
|
password: '',
|
||||||
|
status : null,
|
||||||
|
};
|
||||||
|
this.cleanseChannelInput = this.cleanseChannelInput.bind(this);
|
||||||
|
this.handleChannelInput = this.handleChannelInput.bind(this);
|
||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
|
this.updateIsChannelAvailable = this.updateIsChannelAvailable.bind(this);
|
||||||
|
this.checkIsChannelAvailable = this.checkIsChannelAvailable.bind(this);
|
||||||
|
this.checkIsPasswordProvided = this.checkIsPasswordProvided.bind(this);
|
||||||
|
this.makePublishChannelRequest = this.makePublishChannelRequest.bind(this);
|
||||||
|
this.createChannel = this.createChannel.bind(this);
|
||||||
|
}
|
||||||
|
cleanseChannelInput (input) {
|
||||||
|
input = input.replace(/\s+/g, '-'); // replace spaces with dashes
|
||||||
|
input = input.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-'
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
handleChannelInput (event) {
|
||||||
|
let value = event.target.value;
|
||||||
|
value = this.cleanseChannelInput(value);
|
||||||
|
this.setState({channel: value});
|
||||||
|
if (value) {
|
||||||
|
this.updateIsChannelAvailable(value);
|
||||||
|
} else {
|
||||||
|
this.setState({error: 'Please enter a channel name'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleInput (event) {
|
||||||
|
const name = event.target.name;
|
||||||
|
const value = event.target.value;
|
||||||
|
this.setState({[name]: value});
|
||||||
|
}
|
||||||
|
updateIsChannelAvailable (channel) {
|
||||||
|
const that = this;
|
||||||
|
const channelWithAtSymbol = `@${channel}`;
|
||||||
|
request(`/api/channel-is-available/${channelWithAtSymbol}`)
|
||||||
|
.then(isAvailable => {
|
||||||
|
if (isAvailable) {
|
||||||
|
that.setState({'error': null});
|
||||||
|
} else {
|
||||||
|
that.setState({'error': 'That channel has already been claimed'});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
that.setState({'error': error.message});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
checkIsChannelAvailable (channel) {
|
||||||
|
const channelWithAtSymbol = `@${channel}`;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request(`/api/channel-is-available/${channelWithAtSymbol}`)
|
||||||
|
.then(isAvailable => {
|
||||||
|
console.log('checkIsChannelAvailable result:', isAvailable);
|
||||||
|
if (!isAvailable) {
|
||||||
|
return reject(new Error('That channel has already been claimed'));
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
checkIsPasswordProvided () {
|
||||||
|
const password = this.state.password;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!password || password.length < 1) {
|
||||||
|
console.log('password not provided');
|
||||||
|
return reject(new Error('Please provide a password'));
|
||||||
|
}
|
||||||
|
console.log('password provided');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
makePublishChannelRequest (username, password) {
|
||||||
|
const params = {
|
||||||
|
method : 'POST',
|
||||||
|
body : JSON.stringify({username, password}),
|
||||||
|
headers: new Headers({
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}),
|
||||||
|
credentials: 'include',
|
||||||
|
};
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request('/signup', params)
|
||||||
|
.then(result => {
|
||||||
|
console.log('makePublishChannelRequest result:', result);
|
||||||
|
return resolve(result);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('create channel request failed:', error);
|
||||||
|
reject(new Error('Unfortunately, we encountered an error while creating your channel. Please let us know in Discord!'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
createChannel (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const that = this;
|
||||||
|
this.checkIsPasswordProvided()
|
||||||
|
.then(() => {
|
||||||
|
return that.checkIsChannelAvailable(that.state.channel, that.state.password);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
that.setState({status: 'We are publishing your new channel. Sit tight...'});
|
||||||
|
return that.makePublishChannelRequest(that.state.channel, that.state.password);
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
that.setState({status: null});
|
||||||
|
that.props.onChannelLogin(result.channelName, result.shortChannelId, result.channelClaimId);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
that.setState({'error': error.message, status: null});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{ !this.state.status ? (
|
||||||
|
<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="column column--3 column--sml-10">
|
||||||
|
<label className="label" htmlFor="new-channel-name">Name:</label>
|
||||||
|
</div><div className="column column--6 column--sml-10">
|
||||||
|
<div className="input-text--primary flex-container--row flex-container--left-bottom span--relative">
|
||||||
|
<span>@</span>
|
||||||
|
<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.error && <span id="input-success-channel-name" className="info-message--failure span--absolute">{'\u2716'}</span> }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row row--wide row--short">
|
||||||
|
<div className="column column--3 column--sml-10">
|
||||||
|
<label className="label" htmlFor="new-channel-password">Password:</label>
|
||||||
|
</div><div className="column column--6 column--sml-10">
|
||||||
|
<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} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row row--wide">
|
||||||
|
<button className="button--primary" onClick={this.createChannel}>Create Channel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<p className="fine-print">{this.state.status}</p>
|
||||||
|
<ProgressBar size={12}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChannelCreateForm;
|
15
react/containers/ChannelLoginForm/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { updateLoggedInChannel } from 'actions/channel';
|
||||||
|
import View from './view';
|
||||||
|
import {updateSelectedChannel} from '../../actions/publish';
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onChannelLogin: (name, shortId, longId) => {
|
||||||
|
dispatch(updateLoggedInChannel(name, shortId, longId));
|
||||||
|
dispatch(updateSelectedChannel(name));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(View);
|
81
react/containers/ChannelLoginForm/view.jsx
Normal file
|
@ -0,0 +1,81 @@
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
import React from 'react';
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
import request from 'utils/request';
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
class ChannelLoginForm extends React.Component {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
constructor (props) {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
super(props);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
this.state = {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
error : null,
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
name : '',
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
password: '',
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
};
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
this.loginToChannel = this.loginToChannel.bind(this);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
handleInput (event) {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
const name = event.target.name;
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
const value = event.target.value;
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
this.setState({[name]: value});
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
loginToChannel (event) {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
event.preventDefault();
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
const params = {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
method : 'POST',
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
body : JSON.stringify({username: this.state.name, password: this.state.password}),
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
headers: new Headers({
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
'Content-Type': 'application/json',
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}),
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
credentials: 'include',
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
const that = this;
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
request('login', params)
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
.then(({success, channelName, shortChannelId, channelClaimId, message}) => {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
console.log('loginToChannel success:', success);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
if (success) {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
that.props.onChannelLogin(channelName, shortChannelId, channelClaimId);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
} else {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
that.setState({'error': message});
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
};
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
})
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
.catch(error => {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
console.log('login error', error);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
if (error.message) {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
that.setState({'error': error.message});
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
} else {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
that.setState({'error': error});
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
});
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
render () {
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
return (
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<form id="channel-login-form">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<p id="login-error-display-element" className="info-message-placeholder info-message--failure">{this.state.error}</p>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="row row--wide row--short">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="column column--3 column--sml-10">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<label className="label" htmlFor="channel-login-name-input">Name:</label>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div><div className="column column--6 column--sml-10">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="input-text--primary flex-container--row flex-container--left-bottom">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<span>@</span>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<input type="text" id="channel-login-name-input" className="input-text" name="name" placeholder="Your Channel Name" value={this.state.channelName} onChange={this.handleInput}/>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="row row--wide row--short">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="column column--3 column--sml-10">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<label className="label" htmlFor="channel-login-password-input" >Password:</label>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div><div className="column column--6 column--sml-10">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="input-text--primary">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<input type="password" id="channel-login-password-input" name="password" className="input-text" placeholder="" value={this.state.channelPassword} onChange={this.handleInput}/>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<div className="row row--wide">
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
<button className="button--primary" onClick={this.loginToChannel}>Authenticate</button>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</div>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
</form>
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
);
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
}
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
|||||||
|
export default ChannelLoginForm;
|
||||||
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik I really like how they use the Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the `render` prop, I think it's a really nice way to interact with React elements
|
27
react/containers/ChannelSelect/index.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {setPublishInChannel, updateSelectedChannel, updateError} from 'actions/publish';
|
||||||
|
import View from './view.jsx';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ channel, publish }) => {
|
||||||
|
return {
|
||||||
|
loggedInChannelName: channel.loggedInChannel.name,
|
||||||
|
publishInChannel : publish.publishInChannel,
|
||||||
|
selectedChannel : publish.selectedChannel,
|
||||||
|
channelError : publish.error.channel,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onPublishInChannelChange: (value) => {
|
||||||
|
dispatch(updateError('channel', null));
|
||||||
|
dispatch(setPublishInChannel(value));
|
||||||
|
},
|
||||||
|
onChannelSelect: (value) => {
|
||||||
|
dispatch(updateError('channel', null));
|
||||||
|
dispatch(updateSelectedChannel(value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
58
react/containers/ChannelSelect/view.jsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
import React from 'react';
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
import ChannelLoginForm from 'containers/ChannelLoginForm';
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
import ChannelCreateForm from 'containers/ChannelCreateForm';
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
import * as states from 'constants/publish_channel_select_states';
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
class ChannelSelect extends React.Component {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
constructor (props) {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
super(props);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
this.toggleAnonymousPublish = this.toggleAnonymousPublish.bind(this);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
this.handleSelection = this.handleSelection.bind(this);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
toggleAnonymousPublish (event) {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
const value = event.target.value;
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
if (value === 'anonymous') {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
this.props.onPublishInChannelChange(false);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
} else {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
this.props.onPublishInChannelChange(true);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
handleSelection (event) {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
const selectedOption = event.target.selectedOptions[0].value;
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
this.props.onChannelSelect(selectedOption);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
render () {
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
return (
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<p id="input-error-channel-select" className="info-message-placeholder info-message--failure">{this.props.channelError}</p>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<form>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<div className="column column--3 column--med-10">
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<input type="radio" name="anonymous-or-channel" id="anonymous-radio" className="input-radio" value="anonymous" checked={!this.props.publishInChannel} onChange={this.toggleAnonymousPublish}/>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<label className="label label--pointer" htmlFor="anonymous-radio">Anonymous</label>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<div className="column column--7 column--med-10">
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<input type="radio" name="anonymous-or-channel" id="channel-radio" className="input-radio" value="in a channel" checked={this.props.publishInChannel} onChange={this.toggleAnonymousPublish}/>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<label className="label label--pointer" htmlFor="channel-radio">In a channel</label>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</form>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
{ this.props.publishInChannel && (
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<div className="column column--3">
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<label className="label" htmlFor="channel-name-select">Channel:</label>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</div><div className="column column--7">
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<select type="text" id="channel-name-select" className="select select--arrow" value={this.props.selectedChannel} onChange={this.handleSelection}>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
{ this.props.loggedInChannelName && <option value={this.props.loggedInChannelName} id="publish-channel-select-channel-option">{this.props.loggedInChannelName}</option> }
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<option value={states.LOGIN}>Existing</option>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
<option value={states.CREATE}>New</option>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</select>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
{ (this.props.selectedChannel === states.LOGIN) && <ChannelLoginForm /> }
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
{ (this.props.selectedChannel === states.CREATE) && <ChannelCreateForm /> }
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
)}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
</div>
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
);
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
}
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
|||||||
|
export default ChannelSelect;
|
||||||
Same here, this should be moved to Same here, this should be moved to `componentDidMount`
Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state. I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen. Whenever you separate the data into two sources it can cause some weird issues. Instead of reading prop values and adding them to state, you should just use the prop values and not worry about internal state.
I try to avoid internal state whenever possible, one exception is the app homepage, where we store if the user can scroll left or right in a card row. But that isn't dependent on any prop value, just by what is currently on the screen.
Whenever you separate the data into two sources it can cause some weird issues.
@seanyesmunt The issue I am struggling with, is that I (think) I need the @seanyesmunt The issue I am struggling with, is that I (think) I need the `<select>` element to be a controlled component, and if so then it seems like it would be best to use internal state rather than the store, since no other component should care what option is selected unless this component decides to update the state of the store. The reason I am setting the state when the component mounts and when it receives props, is that if another component updates the store when a new channel is logged into, then I want this `<select>` element to re-render with that option selected. Any ideas on how I can approach this differently so that I can eliminate internal state while still accomplishing those goals?
|
25
react/containers/Dropzone/index.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectFile, updateError } from 'actions/publish';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
file : publish.file,
|
||||||
|
thumbnail: publish.metadata.thumbnail,
|
||||||
|
fileError: publish.error.file,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onFileSelect: (file) => {
|
||||||
|
dispatch(selectFile(file));
|
||||||
|
dispatch(updateError('publishSubmit', null));
|
||||||
|
},
|
||||||
|
onFileError: (value) => {
|
||||||
|
dispatch(updateError('file', value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
140
react/containers/Dropzone/view.jsx
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { validateFile } from 'utils/file';
|
||||||
|
import Preview from 'components/Preview';
|
||||||
|
|
||||||
|
class Dropzone extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
dragOver : false,
|
||||||
|
mouseOver : false,
|
||||||
|
dimPreview: false,
|
||||||
|
};
|
||||||
|
this.handleDrop = this.handleDrop.bind(this);
|
||||||
|
this.handleDragOver = this.handleDragOver.bind(this);
|
||||||
|
this.handleDragEnd = this.handleDragEnd.bind(this);
|
||||||
|
this.handleDragEnter = this.handleDragEnter.bind(this);
|
||||||
|
this.handleDragLeave = this.handleDragLeave.bind(this);
|
||||||
|
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
||||||
|
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
this.handleFileInput = this.handleFileInput.bind(this);
|
||||||
|
this.selectFile = this.selectFile.bind(this);
|
||||||
|
}
|
||||||
|
handleDrop (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.setState({dragOver: false});
|
||||||
|
// if dropped items aren't files, reject them
|
||||||
|
const dt = event.dataTransfer;
|
||||||
|
console.log('dt', dt);
|
||||||
|
if (dt.items) {
|
||||||
|
if (dt.items[0].kind == 'file') {
|
||||||
|
const droppedFile = dt.items[0].getAsFile();
|
||||||
|
this.selectFile(droppedFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleDragOver (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
handleDragEnd (event) {
|
||||||
|
var dt = event.dataTransfer;
|
||||||
|
if (dt.items) {
|
||||||
|
for (var i = 0; i < dt.items.length; i++) {
|
||||||
|
dt.items.remove(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event.dataTransfer.clearData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleDragEnter () {
|
||||||
|
this.setState({dragOver: true, dimPreview: true});
|
||||||
|
}
|
||||||
|
handleDragLeave () {
|
||||||
|
this.setState({dragOver: false, dimPreview: false});
|
||||||
|
}
|
||||||
|
handleMouseEnter () {
|
||||||
|
this.setState({mouseOver: true, dimPreview: true});
|
||||||
|
}
|
||||||
|
handleMouseLeave () {
|
||||||
|
this.setState({mouseOver: false, dimPreview: false});
|
||||||
|
}
|
||||||
|
handleClick (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
// trigger file input
|
||||||
|
document.getElementById('file_input').click();
|
||||||
|
}
|
||||||
|
handleFileInput (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const fileList = event.target.files;
|
||||||
|
this.selectFile(fileList[0]);
|
||||||
|
}
|
||||||
|
selectFile (file) {
|
||||||
|
if (file) {
|
||||||
|
try {
|
||||||
|
validateFile(file); // validate the file's name, type, and size
|
||||||
|
} catch (error) {
|
||||||
|
return this.props.onFileError(error.message);
|
||||||
|
}
|
||||||
|
// stage it so it will be ready when the publish button is clicked
|
||||||
|
this.props.onFileError(null);
|
||||||
|
this.props.onFileSelect(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className="row row--tall flex-container--column">
|
||||||
|
<form>
|
||||||
|
<input className="input-file" type="file" id="file_input" name="file_input" accept="video/*,image/*" onChange={this.handleFileInput} encType="multipart/form-data"/>
|
||||||
|
</form>
|
||||||
|
<div id="preview-dropzone" className={'row row--padded row--tall dropzone' + (this.state.dragOver ? ' dropzone--drag-over' : '')} onDrop={this.handleDrop} onDragOver={this.handleDragOver} onDragEnd={this.handleDragEnd} onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick}>
|
||||||
|
{this.props.file ? (
|
||||||
|
<div>
|
||||||
|
<Preview
|
||||||
|
dimPreview={this.state.dimPreview}
|
||||||
|
file={this.props.file}
|
||||||
|
thumbnail={this.props.thumbnail}
|
||||||
|
/>
|
||||||
|
<div id="dropzone-text-holder" className={'flex-container--column flex-container--center-center'}>
|
||||||
|
{ this.state.dragOver ? (
|
||||||
|
<div id="dropzone-dragover">
|
||||||
|
<p className="blue">Drop it.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
null
|
||||||
|
)}
|
||||||
|
{ this.state.mouseOver ? (
|
||||||
|
<div id="dropzone-instructions">
|
||||||
|
<p className="info-message-placeholder info-message--failure" id="input-error-file-selection">{this.props.fileError}</p>
|
||||||
|
<p>Drag & drop image or video here to publish</p>
|
||||||
|
<p className="fine-print">OR</p>
|
||||||
|
<p className="blue--underlined">CHOOSE FILE</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
null
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div id="dropzone-text-holder" className={'flex-container--column flex-container--center-center'}>
|
||||||
|
{ this.state.dragOver ? (
|
||||||
|
<div id="dropzone-dragover">
|
||||||
|
<p className="blue">Drop it.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div id="dropzone-instructions">
|
||||||
|
<p className="info-message-placeholder info-message--failure" id="input-error-file-selection">{this.props.fileError}</p>
|
||||||
|
<p>Drag & drop image or video here to publish</p>
|
||||||
|
<p className="fine-print">OR</p>
|
||||||
|
<p className="blue--underlined">CHOOSE FILE</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dropzone;
|
23
react/containers/NavBar/index.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { updateLoggedInChannel } from 'actions/channel';
|
||||||
|
import View from './view';
|
||||||
|
import {updateSelectedChannel} from '../../actions/publish';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ channel }) => {
|
||||||
|
return {
|
||||||
|
channelName : channel.loggedInChannel.name,
|
||||||
|
channelShortId: channel.loggedInChannel.shortId,
|
||||||
|
channelLongId : channel.loggedInChannel.longId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onChannelLogin: (name, shortId, longId) => {
|
||||||
|
dispatch(updateLoggedInChannel(name, shortId, longId));
|
||||||
|
dispatch(updateSelectedChannel(name));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
85
react/containers/NavBar/view.jsx
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import React from 'react';
|
||||||
|
import request from 'utils/request';
|
||||||
|
import Logo from 'components/Logo';
|
||||||
|
import NavBarChannelDropdown from 'components/NavBarChannelOptionsDropdown';
|
||||||
|
|
||||||
|
const VIEW = 'VIEW';
|
||||||
|
const LOGOUT = 'LOGOUT';
|
||||||
|
|
||||||
|
class NavBar extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.checkForLoggedInUser = this.checkForLoggedInUser.bind(this);
|
||||||
|
this.logoutUser = this.logoutUser.bind(this);
|
||||||
|
this.handleSelection = this.handleSelection.bind(this);
|
||||||
|
}
|
||||||
|
componentDidMount () {
|
||||||
|
// check to see if the user is already logged in
|
||||||
|
this.checkForLoggedInUser();
|
||||||
|
}
|
||||||
|
checkForLoggedInUser () {
|
||||||
|
// check for whether a channel is already logged in
|
||||||
|
const params = {
|
||||||
|
credentials: 'include',
|
||||||
|
}
|
||||||
|
request('/user', params)
|
||||||
|
.then(({success, message}) => {
|
||||||
|
if (success) {
|
||||||
|
this.props.onChannelLogin(message.channelName, message.shortChannelId, message.channelClaimId);
|
||||||
|
} else {
|
||||||
|
console.log('user was not logged in');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('authenticate user errored:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logoutUser () {
|
||||||
|
// send logout request to server
|
||||||
|
window.location.href = '/logout'; // NOTE: replace with a call to the server
|
||||||
|
}
|
||||||
|
handleSelection (event) {
|
||||||
|
console.log('handling selection', event);
|
||||||
|
const value = event.target.selectedOptions[0].value;
|
||||||
|
console.log('value', value);
|
||||||
|
switch (value) {
|
||||||
|
case LOGOUT:
|
||||||
|
this.logoutUser();
|
||||||
|
break;
|
||||||
|
case VIEW:
|
||||||
|
// redirect to channel page
|
||||||
|
window.location.href = `/${this.props.channelName}:${this.props.channelLongId}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className="row row--wide nav-bar">
|
||||||
|
<div className="row row--padded row--short flex-container--row flex-container--space-between-center">
|
||||||
|
<Logo />
|
||||||
|
<div className="nav-bar--center">
|
||||||
|
<span className="nav-bar-tagline">Open-source, decentralized image and video sharing.</span>
|
||||||
|
</div>
|
||||||
|
<div className="nav-bar--right">
|
||||||
|
<a className="nav-bar-link link--nav-active" href="/">Publish</a>
|
||||||
|
<a className="nav-bar-link link--nav" href="/about">About</a>
|
||||||
|
{ this.props.channelName ? (
|
||||||
|
<NavBarChannelDropdown
|
||||||
|
channelName={this.props.channelName}
|
||||||
|
handleSelection={this.handleSelection}
|
||||||
|
VIEW={VIEW}
|
||||||
|
LOGOUT={LOGOUT}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<a id="nav-bar-login-link" className="nav-bar-link link--nav" href="/login">Channel</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavBar;
|
47
react/containers/PublishForm/index.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {clearFile, selectFile, updateError, updatePublishStatus} from 'actions/publish';
|
||||||
|
import {updateLoggedInChannel} from 'actions/channel';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ channel, publish }) => {
|
||||||
|
return {
|
||||||
|
loggedInChannel : channel.loggedInChannel,
|
||||||
|
file : publish.file,
|
||||||
|
claim : publish.claim,
|
||||||
|
title : publish.metadata.title,
|
||||||
|
thumbnail : publish.metadata.thumbnail,
|
||||||
|
description : publish.metadata.description,
|
||||||
|
license : publish.metadata.license,
|
||||||
|
nsfw : publish.metadata.nsfw,
|
||||||
|
publishInChannel : publish.publishInChannel,
|
||||||
|
selectedChannel : publish.selectedChannel,
|
||||||
|
fileError : publish.error.file,
|
||||||
|
urlError : publish.error.url,
|
||||||
|
publishSubmitError: publish.error.publishSubmit,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onFileSelect: (file) => {
|
||||||
|
dispatch(selectFile(file));
|
||||||
|
},
|
||||||
|
onFileClear: () => {
|
||||||
|
dispatch(clearFile());
|
||||||
|
},
|
||||||
|
onChannelLogin: (name, shortId, longId) => {
|
||||||
|
dispatch(updateLoggedInChannel(name, shortId, longId));
|
||||||
|
},
|
||||||
|
onPublishStatusChange: (status, message) => {
|
||||||
|
dispatch(updatePublishStatus(status, message));
|
||||||
|
},
|
||||||
|
onChannelSelectionError: (value) => {
|
||||||
|
dispatch(updateError('channel', value));
|
||||||
|
},
|
||||||
|
onPublishSubmitError: (value) => {
|
||||||
|
dispatch(updateError('publishSubmit', value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
179
react/containers/PublishForm/view.jsx
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Dropzone from 'containers/Dropzone';
|
||||||
|
import PublishTitleInput from 'containers/PublishTitleInput';
|
||||||
|
import PublishUrlInput from 'containers/PublishUrlInput';
|
||||||
|
import PublishThumbnailInput from 'containers/PublishThumbnailInput';
|
||||||
|
import PublishMetadataInputs from 'containers/PublishMetadataInputs';
|
||||||
|
import ChannelSelect from 'containers/ChannelSelect';
|
||||||
|
import * as publishStates from 'constants/publish_claim_states';
|
||||||
|
|
||||||
|
class PublishForm extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.validateChannelSelection = this.validateChannelSelection.bind(this);
|
||||||
|
this.validatePublishParams = this.validatePublishParams.bind(this);
|
||||||
|
this.makePublishRequest = this.makePublishRequest.bind(this);
|
||||||
|
this.publish = this.publish.bind(this);
|
||||||
|
}
|
||||||
|
validateChannelSelection () {
|
||||||
|
console.log('validating channel selection');
|
||||||
|
// make sure all required data is provided
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// if publishInChannel is true, is a channel selected & logged in?
|
||||||
|
if (this.props.publishInChannel && (this.props.selectedChannel !== this.props.loggedInChannel.name)) {
|
||||||
|
// update state with error
|
||||||
|
this.props.onChannelSelectionError('Log in to a channel or select Anonymous"');
|
||||||
|
// reject this promise
|
||||||
|
return reject(new Error('Fix the channel'));
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
validatePublishParams () {
|
||||||
|
console.log('validating publish params');
|
||||||
|
// make sure all required data is provided
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// is there a file?
|
||||||
|
if (!this.props.file) {
|
||||||
|
return reject(new Error('Please choose a file'));
|
||||||
|
}
|
||||||
|
// is there a claim chosen?
|
||||||
|
if (!this.props.claim) {
|
||||||
|
return reject(new Error('Please enter a URL'));
|
||||||
|
}
|
||||||
|
if (this.props.urlError) {
|
||||||
|
return reject(new Error('Fix the url'));
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
makePublishRequest (file, metadata) {
|
||||||
|
console.log('making publish request');
|
||||||
|
const uri = '/api/claim-publish';
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
const fd = this.appendDataToFormData(file, metadata);
|
||||||
|
const that = this;
|
||||||
|
xhr.upload.addEventListener('loadstart', function () {
|
||||||
|
that.props.onPublishStatusChange(publishStates.LOAD_START, 'upload started');
|
||||||
|
});
|
||||||
|
xhr.upload.addEventListener('progress', function (e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
const percentage = Math.round((e.loaded * 100) / e.total);
|
||||||
|
console.log('progress:', percentage);
|
||||||
|
that.props.onPublishStatusChange(publishStates.LOADING, `${percentage}%`);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
xhr.upload.addEventListener('load', function () {
|
||||||
|
console.log('loaded 100%');
|
||||||
|
that.props.onPublishStatusChange(publishStates.PUBLISHING, null);
|
||||||
|
}, false);
|
||||||
|
xhr.open('POST', uri, true);
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState === 4) {
|
||||||
|
console.log('publish response:', xhr.response);
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
console.log('publish complete!');
|
||||||
|
const url = JSON.parse(xhr.response).message.url;
|
||||||
|
that.props.onPublishStatusChange(publishStates.SUCCESS, url);
|
||||||
|
window.location = url;
|
||||||
|
} else if (xhr.status === 502) {
|
||||||
|
that.props.onPublishStatusChange(publishStates.FAILED, 'Spee.ch was not able to get a response from the LBRY network.');
|
||||||
|
} else {
|
||||||
|
that.props.onPublishStatusChange(publishStates.FAILED, JSON.parse(xhr.response).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Initiate a multipart/form-data upload
|
||||||
|
xhr.send(fd);
|
||||||
|
}
|
||||||
|
createMetadata () {
|
||||||
|
console.log('creating metadata');
|
||||||
|
let metadata = {
|
||||||
|
name : this.props.claim,
|
||||||
|
title : this.props.title,
|
||||||
|
description: this.props.description,
|
||||||
|
license : this.props.license,
|
||||||
|
nsfw : this.props.nsfw,
|
||||||
|
type : this.props.file.type,
|
||||||
|
thumbnail : this.props.thumbnail,
|
||||||
|
};
|
||||||
|
if (this.props.publishInChannel) {
|
||||||
|
metadata['channelName'] = this.props.selectedChannel;
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
appendDataToFormData (file, metadata) {
|
||||||
|
var fd = new FormData();
|
||||||
|
fd.append('file', file);
|
||||||
|
for (var key in metadata) {
|
||||||
|
if (metadata.hasOwnProperty(key)) {
|
||||||
|
console.log('adding form data', key, metadata[key]);
|
||||||
|
fd.append(key, metadata[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
publish () {
|
||||||
|
console.log('publishing file');
|
||||||
|
// publish the asset
|
||||||
|
const that = this;
|
||||||
|
this.validateChannelSelection()
|
||||||
|
.then(() => {
|
||||||
|
return that.validatePublishParams();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const metadata = that.createMetadata();
|
||||||
|
// publish the claim
|
||||||
|
return that.makePublishRequest(that.props.file, metadata);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
that.props.onPublishStatusChange('publish request made');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
that.props.onPublishSubmitError(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className="row row--no-bottom">
|
||||||
|
<div className="column column--10">
|
||||||
|
<PublishTitleInput />
|
||||||
|
</div>
|
||||||
|
<div className="column column--5 column--sml-10" >
|
||||||
|
<div className="row row--padded">
|
||||||
|
<Dropzone />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="column column--5 column--sml-10 align-content-top">
|
||||||
|
<div id="publish-active-area" className="row row--padded">
|
||||||
|
<div className="row row--padded row--no-top row--wide">
|
||||||
|
<PublishUrlInput />
|
||||||
|
</div>
|
||||||
|
<div className="row row--padded row--no-top row--wide">
|
||||||
|
<ChannelSelect />
|
||||||
|
</div>
|
||||||
|
{ (this.props.file.type === 'video/mp4') && (
|
||||||
|
<div className="row row--padded row--no-top row--wide ">
|
||||||
|
<PublishThumbnailInput />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="row row--padded row--no-top row--no-bottom row--wide">
|
||||||
|
<PublishMetadataInputs />
|
||||||
|
</div>
|
||||||
|
<div className="row row--wide align-content-center">
|
||||||
|
<button id="publish-submit" className="button--primary button--large" onClick={this.publish}>Publish</button>
|
||||||
|
</div>
|
||||||
|
<div className="row row--padded row--no-bottom align-content-center">
|
||||||
|
<button className="button--cancel" onClick={this.props.onFileClear}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
<div className="row row--short align-content-center">
|
||||||
|
<p className="fine-print">By clicking 'Publish', you affirm that you have the rights to publish this content to the LBRY network, and that you understand the properties of publishing it to a decentralized, user-controlled network. <a className="link--primary" target="_blank" href="https://lbry.io/learn">Read more.</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublishForm;
|
25
react/containers/PublishMetadataInputs/index.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {updateMetadata, toggleMetadataInputs} from 'actions/publish';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
showMetadataInputs: publish.showMetadataInputs,
|
||||||
|
description : publish.metadata.description,
|
||||||
|
license : publish.metadata.license,
|
||||||
|
nsfw : publish.metadata.nsfw,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onMetadataChange: (name, value) => {
|
||||||
|
dispatch(updateMetadata(name, value));
|
||||||
|
},
|
||||||
|
onToggleMetadataInputs: (value) => {
|
||||||
|
dispatch(toggleMetadataInputs(value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
74
react/containers/PublishMetadataInputs/view.jsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ExpandingTextArea from 'components/ExpandingTextArea';
|
||||||
|
|
||||||
|
class PublishMetadataInputs extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.toggleShowInputs = this.toggleShowInputs.bind(this);
|
||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
|
this.handleSelect = this.handleSelect.bind(this);
|
||||||
|
}
|
||||||
|
toggleShowInputs () {
|
||||||
|
this.props.onToggleMetadataInputs(!this.props.showMetadataInputs);
|
||||||
|
}
|
||||||
|
handleInput (event) {
|
||||||
|
const target = event.target;
|
||||||
|
const value = target.type === 'checkbox' ? target.checked : target.value;
|
||||||
|
const name = target.name;
|
||||||
|
this.props.onMetadataChange(name, value);
|
||||||
|
}
|
||||||
|
handleSelect (event) {
|
||||||
|
const name = event.target.name;
|
||||||
|
const selectedOption = event.target.selectedOptions[0].value;
|
||||||
|
this.props.onMetadataChange(name, selectedOption);
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div id="publish-details" className="row row--padded row--no-top row--wide">
|
||||||
|
{this.props.showMetadataInputs && (
|
||||||
|
<div>
|
||||||
|
<div className="row row--no-top">
|
||||||
|
<div className="column column--3 column--med-10 align-content-top">
|
||||||
|
<label htmlFor="publish-license" className="label">Description:</label>
|
||||||
|
</div><div className="column column--7 column--sml-10">
|
||||||
|
<ExpandingTextArea
|
||||||
|
id="publish-description"
|
||||||
|
className="textarea textarea--primary textarea--full-width"
|
||||||
|
rows={1}
|
||||||
|
maxLength={2000}
|
||||||
|
style={{ maxHeight: 200 }}
|
||||||
|
name="description"
|
||||||
|
placeholder="Optional description"
|
||||||
|
value={this.props.description}
|
||||||
|
onChange={this.handleInput} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row row--no-top">
|
||||||
|
<div className="column column--3 column--med-10">
|
||||||
|
<label htmlFor="publish-license" className="label">License:</label>
|
||||||
|
</div><div className="column column--7 column--sml-10">
|
||||||
|
<select type="text" name="license" id="publish-license" className="select select--primary" onChange={this.handleSelect}>
|
||||||
|
<option value=" ">Unspecified</option>
|
||||||
|
<option value="Public Domain">Public Domain</option>
|
||||||
|
<option value="Creative Commons">Creative Commons</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="row row--no-top">
|
||||||
|
<div className="column column--3">
|
||||||
|
<label htmlFor="publish-nsfw" className="label">Mature:</label>
|
||||||
|
</div><div className="column column--7">
|
||||||
|
<input className="input-checkbox" type="checkbox" id="publish-nsfw" name="nsfw" value={this.props.nsfw} onChange={this.handleInput} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<a className="label link--primary" id="publish-details-toggle" href="#" onClick={this.toggleShowInputs}>{this.props.showMetadataInputs ? '[less]' : '[more]'}</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublishMetadataInputs;
|
19
react/containers/PublishThumbnailInput/index.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {updateMetadata} from 'actions/publish';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
thumbnail: publish.metadata.thumbnail,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onThumbnailChange: (name, value) => {
|
||||||
|
dispatch(updateMetadata(name, value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
83
react/containers/PublishThumbnailInput/view.jsx
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class PublishThumbnailInput extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
videoPreviewSrc: null,
|
||||||
|
thumbnailError : null,
|
||||||
|
thumbnailInput : '',
|
||||||
|
}
|
||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
|
this.urlIsAnImage = this.urlIsAnImage.bind(this);
|
||||||
|
this.testImage = this.testImage.bind(this);
|
||||||
|
this.updateVideoThumb = this.updateVideoThumb.bind(this);
|
||||||
|
}
|
||||||
|
handleInput (event) {
|
||||||
|
const value = event.target.value;
|
||||||
|
this.setState({thumbnailInput: value});
|
||||||
|
}
|
||||||
|
urlIsAnImage (url) {
|
||||||
|
return (url.match(/\.(jpeg|jpg|gif|png)$/) != null);
|
||||||
|
}
|
||||||
|
testImage (url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open('HEAD', url, true);
|
||||||
|
xhttp.onreadystatechange = () => {
|
||||||
|
if (xhttp.readyState === 4) {
|
||||||
|
if (xhttp.status === 200) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhttp.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
updateVideoThumb (event) {
|
||||||
|
const imageUrl = event.target.value;
|
||||||
|
const that = this;
|
||||||
|
if (this.urlIsAnImage(imageUrl)) {
|
||||||
|
this.testImage(imageUrl, 3000)
|
||||||
|
.then(() => {
|
||||||
|
console.log('thumbnail is a valid image');
|
||||||
|
that.props.onThumbnailChange('thumbnail', imageUrl);
|
||||||
|
that.setState({thumbnailError: null});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('encountered an error loading thumbnail image url:', error);
|
||||||
|
that.props.onThumbnailChange('thumbnail', null);
|
||||||
|
that.setState({thumbnailError: 'That is an invalid image url'});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
that.props.onThumbnailChange('thumbnail', null);
|
||||||
|
that.setState({thumbnailError: null});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="column column--3 column--sml-10">
|
||||||
|
<label className="label">Thumbnail:</label>
|
||||||
|
</div><div className="column column--6 column--sml-10">
|
||||||
|
<div className="input-text--primary">
|
||||||
|
<p className="info-message-placeholder info-message--failure">{this.state.thumbnailError}</p>
|
||||||
|
<input
|
||||||
|
type="text" id="claim-thumbnail-input"
|
||||||
|
className="input-text input-text--full-width"
|
||||||
|
placeholder="https://spee.ch/xyz/example.jpg"
|
||||||
|
value={this.state.thumbnailInput}
|
||||||
|
onChange={ (event) => {
|
||||||
|
this.handleInput(event);
|
||||||
|
this.updateVideoThumb(event);
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublishThumbnailInput;
|
19
react/containers/PublishTitleInput/index.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import {updateMetadata} from 'actions/publish';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
title: publish.metadata.title,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onMetadataChange: (name, value) => {
|
||||||
|
dispatch(updateMetadata(name, value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
20
react/containers/PublishTitleInput/view.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class PublishTitleInput extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
|
}
|
||||||
|
handleInput (e) {
|
||||||
|
const name = e.target.name;
|
||||||
|
const value = e.target.value;
|
||||||
|
this.props.onMetadataChange(name, value);
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<input type="text" id="publish-title" className="input-text text--large input-text--full-width" name="title" placeholder="Give your post a title..." onChange={this.handleInput} value={this.props.title}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublishTitleInput;
|
12
react/containers/PublishTool/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
file : publish.file,
|
||||||
|
status : publish.status.status,
|
||||||
|
message: publish.status.message,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(View);
|
25
react/containers/PublishTool/view.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Dropzone from 'containers/Dropzone';
|
||||||
|
import PublishForm from 'containers/PublishForm';
|
||||||
|
import PublishStatus from 'components/PublishStatus';
|
||||||
|
|
||||||
|
class PublishTool extends React.Component {
|
||||||
|
render () {
|
||||||
|
if (this.props.file) {
|
||||||
|
if (this.props.status) {
|
||||||
|
return (
|
||||||
|
<PublishStatus
|
||||||
|
status={this.props.status}
|
||||||
|
message={this.props.message}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <PublishForm />;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return <Dropzone />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublishTool;
|
29
react/containers/PublishUrlInput/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import {updateClaim, updateError} from 'actions/publish';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ channel, publish }) => {
|
||||||
|
return {
|
||||||
|
loggedInChannelName : channel.loggedInChannel.name,
|
||||||
|
loggedInChannelShortId: channel.loggedInChannel.shortId,
|
||||||
|
fileName : publish.file.name,
|
||||||
|
publishInChannel : publish.publishInChannel,
|
||||||
|
selectedChannel : publish.selectedChannel,
|
||||||
|
claim : publish.claim,
|
||||||
|
urlError : publish.error.url,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onClaimChange: (value) => {
|
||||||
|
dispatch(updateClaim(value));
|
||||||
|
dispatch(updateError('publishSubmit', null));
|
||||||
|
},
|
||||||
|
onUrlError: (value) => {
|
||||||
|
dispatch(updateError('url', value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
83
react/containers/PublishUrlInput/view.jsx
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import React from 'react';
|
||||||
|
import request from 'utils/request';
|
||||||
|
import UrlMiddle from 'components/PublishUrlMiddleDisplay';
|
||||||
|
|
||||||
|
class PublishUrlInput extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
|
this.cleanseInput = this.cleanseInput.bind(this);
|
||||||
|
this.setClaimNameFromFileName = this.setClaimNameFromFileName.bind(this);
|
||||||
|
this.checkClaimIsAvailable = this.checkClaimIsAvailable.bind(this);
|
||||||
|
}
|
||||||
|
componentDidMount () {
|
||||||
|
if (!this.props.claim || this.props.claim === '') {
|
||||||
|
this.setClaimNameFromFileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentWillReceiveProps ({claim: newClaim}) {
|
||||||
|
if (newClaim) {
|
||||||
|
this.checkClaimIsAvailable(newClaim);
|
||||||
|
} else {
|
||||||
|
this.props.onUrlError('Please enter a URL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleInput (event) {
|
||||||
|
let value = event.target.value;
|
||||||
|
value = this.cleanseInput(value);
|
||||||
|
// update the state
|
||||||
|
this.props.onClaimChange(value);
|
||||||
|
}
|
||||||
|
cleanseInput (input) {
|
||||||
|
input = input.replace(/\s+/g, '-'); // replace spaces with dashes
|
||||||
|
input = input.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-'
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
setClaimNameFromFileName () {
|
||||||
|
const fileName = this.props.fileName;
|
||||||
|
const fileNameWithoutEnding = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
|
const cleanClaimName = this.cleanseInput(fileNameWithoutEnding);
|
||||||
|
this.props.onClaimChange(cleanClaimName);
|
||||||
|
}
|
||||||
|
checkClaimIsAvailable (claim) {
|
||||||
|
const that = this;
|
||||||
|
request(`/api/claim-is-available/${claim}`)
|
||||||
|
.then(isAvailable => {
|
||||||
|
// console.log('checkClaimIsAvailable request response:', isAvailable);
|
||||||
|
if (isAvailable) {
|
||||||
|
that.props.onUrlError(null);
|
||||||
|
} else {
|
||||||
|
that.props.onUrlError('That url has already been claimed');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
that.props.onUrlError(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p id="input-error-claim-name" className="info-message-placeholder info-message--failure">{this.props.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">spee.ch / </span>
|
||||||
|
|
||||||
|
<UrlMiddle
|
||||||
|
publishInChannel={this.props.publishInChannel}
|
||||||
|
selectedChannel={this.props.selectedChannel}
|
||||||
|
loggedInChannelName={this.props.loggedInChannelName}
|
||||||
|
loggedInChannelShortId={this.props.loggedInChannelShortId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input type="text" id="claim-name-input" className="input-text" name='claim' placeholder="your-url-here" onChange={this.handleInput} value={this.props.claim}/>
|
||||||
|
{ (this.props.claim && !this.props.urlError) && <span id="input-success-claim-name" className="info-message--success span--absolute">{'\u2713'}</span> }
|
||||||
|
{ this.props.urlError && <span id="input-success-channel-name" className="info-message--failure span--absolute">{'\u2716'}</span> }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublishUrlInput;
|
28
react/reducers/channel.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import * as actions from 'constants/channel_action_types';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
loggedInChannel: {
|
||||||
|
name : null,
|
||||||
|
shortId: null,
|
||||||
|
longId : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reducers describe how the application's state changes in response to actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function (state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case actions.CHANNEL_UPDATE:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
loggedInChannel: {
|
||||||
|
name : action.name,
|
||||||
|
shortId: action.shortId,
|
||||||
|
longId : action.longId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
8
react/reducers/index.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
import { combineReducers } from 'redux';
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
import PublishReducer from 'reducers/publish';
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
import ChannelReducer from 'reducers/channel';
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
export default combineReducers({
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
channel: ChannelReducer,
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
publish: PublishReducer,
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
|||||||
|
});
|
||||||
`import * as`
Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision). Some of these should possibly be renamed or refactored into separate files (or possibly can wait until next refactor/revision).
|
79
react/reducers/publish.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import * as actions from 'constants/publish_action_types';
|
||||||
|
import { LOGIN } from 'constants/publish_channel_select_states';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
publishInChannel : false,
|
||||||
|
selectedChannel : LOGIN,
|
||||||
|
showMetadataInputs: false,
|
||||||
|
status : {
|
||||||
|
status : null,
|
||||||
|
message: null,
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
file : null,
|
||||||
|
url : null,
|
||||||
|
channel : null,
|
||||||
|
publishSubmit: null,
|
||||||
|
},
|
||||||
|
file : null,
|
||||||
|
claim : '',
|
||||||
|
metadata: {
|
||||||
|
title : '',
|
||||||
|
thumbnail : '',
|
||||||
|
description: '',
|
||||||
|
license : '',
|
||||||
|
nsfw : false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reducers describe how the application's state changes in response to actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function (state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case actions.FILE_SELECTED:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
file: action.file,
|
||||||
|
});
|
||||||
|
case actions.FILE_CLEAR:
|
||||||
|
return initialState;
|
||||||
|
case actions.METADATA_UPDATE:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
metadata: Object.assign({}, state.metadata, {
|
||||||
|
[action.name]: action.value,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case actions.CLAIM_UPDATE:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
claim: action.value,
|
||||||
|
});
|
||||||
|
case actions.SET_PUBLISH_IN_CHANNEL:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
publishInChannel: action.channel,
|
||||||
|
});
|
||||||
|
case actions.PUBLISH_STATUS_UPDATE:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
status: Object.assign({}, state.status, {
|
||||||
|
status : action.status,
|
||||||
|
message: action.message,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case actions.ERROR_UPDATE:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
error: Object.assign({}, state.error, {
|
||||||
|
[action.name]: action.value,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case actions.SELECTED_CHANNEL_UPDATE:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
selectedChannel: action.value,
|
||||||
|
});
|
||||||
|
case actions.TOGGLE_METADATA_INPUTS:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
showMetadataInputs: action.value,
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
38
react/utils/file.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
module.exports = {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
validateFile (file) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
if (!file) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
console.log('no file found');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
throw new Error('no file provided');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
if (/'/.test(file.name)) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
console.log('file name had apostrophe in it');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
throw new Error('apostrophes are not allowed in the file name');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
// validate size and type
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
switch (file.type) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
case 'image/jpeg':
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
case 'image/jpg':
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
case 'image/png':
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
if (file.size > 10000000) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
console.log('file was too big');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
throw new Error('Sorry, images are limited to 10 megabytes.');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
break;
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
case 'image/gif':
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
if (file.size > 50000000) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
console.log('file was too big');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
throw new Error('Sorry, GIFs are limited to 50 megabytes.');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
break;
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
case 'video/mp4':
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
if (file.size > 50000000) {
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
console.log('file was too big');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
throw new Error('Sorry, videos are limited to 50 megabytes.');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
break;
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
default:
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
console.log('file type is not supported');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.');
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
},
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
|||||||
|
}
|
||||||
GIFs GIFs
I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear. I'm interested in the direction of this PR, it seems to be purposed as an image sharing service as per required. .png .jpeg .gif ... requirements. Can we still publish video and is there support for generic file types? It just feels like its moving away from video upload and I wanted to be clear.
@etisdew yes @etisdew yes `video/mp4` is supported and supporting video uploads is a key goal for spee.ch
|
44
react/utils/request.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* Parses the JSON returned by a network request
|
||||||
|
*
|
||||||
|
* @param {object} response A response from a network request
|
||||||
|
*
|
||||||
|
* @return {object} The parsed JSON from the request
|
||||||
|
*/
|
||||||
|
function parseJSON (response) {
|
||||||
|
if (response.status === 204 || response.status === 205) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a network request came back fine, and throws an error if not
|
||||||
|
*
|
||||||
|
* @param {object} response A response from a network request
|
||||||
|
*
|
||||||
|
* @return {object|undefined} Returns either the response, or throws an error
|
||||||
|
*/
|
||||||
|
function checkStatus (response) {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = new Error(response.statusText);
|
||||||
|
error.response = response;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests a URL, returning a promise
|
||||||
|
*
|
||||||
|
* @param {string} url The URL we want to request
|
||||||
|
* @param {object} [options] The options we want to pass to "fetch"
|
||||||
|
*
|
||||||
|
* @return {object} The response data
|
||||||
|
*/
|
||||||
|
export default function request (url, options) {
|
||||||
|
return fetch(url, options)
|
||||||
|
.then(checkStatus)
|
||||||
|
.then(parseJSON);
|
||||||
|
}
|
|
@ -71,7 +71,6 @@ module.exports = (app) => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
res.status(200).json(true);
|
res.status(200).json(true);
|
||||||
} else {
|
} else {
|
||||||
// logger.debug(`Rejecting '${params.name}' because that name has already been claimed by this site`);
|
|
||||||
res.status(200).json(false);
|
res.status(200).json(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -86,7 +85,6 @@ module.exports = (app) => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
res.status(200).json(true);
|
res.status(200).json(true);
|
||||||
} else {
|
} else {
|
||||||
// logger.debug(`Rejecting '${params.name}' because that channel has already been claimed`);
|
|
||||||
res.status(200).json(false);
|
res.status(200).json(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,14 +13,38 @@ module.exports = (app) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// route for log in
|
// route for log in
|
||||||
app.post('/login', passport.authenticate('local-login'), (req, res) => {
|
app.post('/login', (req, res, next) => {
|
||||||
// logger.debug('req.user:', req.user); // req.user contains the authenticated user's info
|
passport.authenticate('local-login', (err, user, info) => {
|
||||||
|
logger.debug('info:', info);
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
if (!user) {
|
||||||
|
return res.status(200).json({
|
||||||
|
success: false,
|
||||||
|
message: info.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
logger.debug('successful login');
|
logger.debug('successful login');
|
||||||
res.status(200).json({
|
req.logIn(user, (err) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
return res.status(200).json({
|
||||||
success : true,
|
success : true,
|
||||||
channelName : req.user.channelName,
|
channelName : req.user.channelName,
|
||||||
channelClaimId: req.user.channelClaimId,
|
channelClaimId: req.user.channelClaimId,
|
||||||
shortChannelId: req.user.shortChannelId,
|
shortChannelId: req.user.shortChannelId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
})(req, res, next);
|
||||||
|
});
|
||||||
|
// see if user is authenticated, and return credentials if so
|
||||||
|
app.get('/user', (req, res) => {
|
||||||
|
if (req.user) {
|
||||||
|
res.status(200).json({success: true, message: req.user});
|
||||||
|
} else {
|
||||||
|
res.status(200).json({success: false, message: 'user is not logged in'});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<div class="column column--5 column--med-10 align-content-top">
|
<div class="column column--5 column--med-10 align-content-top">
|
||||||
<div class="column column--8 column--med-10">
|
<div class="column column--8 column--med-10">
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{#ifConditional this.totalPages '===' 0}}
|
{{#ifConditional this.totalPages '===' 0}}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<h3>404: Not Found</h3>
|
<h3>404: Not Found</h3>
|
||||||
<p>That page does not exist. Return <a class="link--primary" href="/">home</a>.</p>
|
<p>That page does not exist. Return <a class="link--primary" href="/">home</a>.</p>
|
||||||
|
|
|
@ -1,52 +1,11 @@
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
|
<div id="react-nav-bar"></div>
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div class="row row--tall flex-container--column">
|
<div class="row row--tall flex-container--column">
|
||||||
<form>
|
<div id="react-publish-tool" class="row row--padded row--tall flex-container--column">
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<input class="input-file" type="file" id="file_input" name="file_input" accept="video/*,image/*" onchange="publishFileFunctions.previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/>
|
<div class="row row--padded row--tall flex-container--column flex-container--center-center">
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</form>
|
<p>loading...</p>
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="primary-dropzone" class="dropzone row row--margined row--padded row--tall flex-container--column flex-container--center-center" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)" ondragenter="dragenter_handler(event)" ondragleave="dragexit_handler(event)" onclick="publishFileFunctions.triggerFileChooser('file_input', event)">
|
{{> progressBar}}
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="primary-dropzone-instructions">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p class="info-message-placeholder info-message--failure" id="input-error-file-selection" hidden="true"></p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p>Drag & drop image or video here to publish</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p class="fine-print">OR</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p class="blue--underlined">CHOOSE FILE</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="dropbzone-dragover" class="hidden">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p class="blue">Drop it.</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="publish-form" class="hidden">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div class="row row--padded row--no-bottom">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div class="column column--10">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<!-- title input -->
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<input type="text" id="publish-title" class="input-text text--large input-text--full-width" placeholder="Give your post a title...">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div class="column column--5 column--sml-10" >
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<!-- preview -->
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div class="row row--padded">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="asset-preview-holder" class="dropzone" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)" ondragenter="preview_onmouseenter_handler()" ondragleave="preview_onmouseleave_handler()" onmouseenter="preview_onmouseenter_handler()" onmouseleave="preview_onmouseleave_handler()" onclick="publishFileFunctions.triggerFileChooser('file_input', event)">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="asset-preview-dropzone-instructions" class="hidden">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p>Drag & drop image or video here</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p class="fine-print">OR</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<p class="blue--underlined">CHOOSE FILE</p>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="asset-preview-target"></div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div><div class="column column--5 column--sml-10 align-content-top">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="publish-active-area" class="row row--padded">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
{{> publishForm-Channel}}
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
{{> publishForm-Url}}
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
{{> publishForm-Thumbnail}}
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
{{> publishForm-Details}}
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
{{> publishForm-Submit}}
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="publish-status" class="hidden">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div class="row row--margined">
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="publish-update" class="row align-content-center"></div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="publish-progress-bar" class="row align-content-center"></div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
<div id="upload-percent" class="row align-content-center"></div>
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
|
<script src="/bundle/bundle.js"></script>
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|||||||
|
|
||||||
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
This doesn't seem exactly on theme. Maybe ask nizuka? This doesn't seem exactly on theme. Maybe ask nizuka?
|
|
@ -17,8 +17,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body id="channel-body">
|
<body id="channel-body">
|
||||||
<script src="/assets/js/generalFunctions.js"></script>
|
<script src="/assets/js/generalFunctions.js"></script>
|
||||||
<script src="/assets/js/navBarFunctions.js"></script>
|
|
||||||
{{> navBar}}
|
|
||||||
{{{ body }}}
|
{{{ body }}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -18,13 +18,6 @@
|
||||||
<body id="main-body">
|
<body id="main-body">
|
||||||
<script src="/assets/js/generalFunctions.js"></script>
|
<script src="/assets/js/generalFunctions.js"></script>
|
||||||
<script src="/assets/js/validationFunctions.js"></script>
|
<script src="/assets/js/validationFunctions.js"></script>
|
||||||
<script src="/assets/js/publishFileFunctions.js"></script>
|
|
||||||
<script src="/assets/js/authFunctions.js"></script>
|
|
||||||
<script src="/assets/js/loginFunctions.js"></script>
|
|
||||||
<script src="/assets/js/dropzoneFunctions.js"></script>
|
|
||||||
<script src="/assets/js/createChannelFunctions.js"></script>
|
|
||||||
<script src="/assets/js/navBarFunctions.js"></script>
|
|
||||||
{{> navBar}}
|
|
||||||
{{{ body }}}
|
{{{ body }}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -14,9 +14,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body id="show-body">
|
<body id="show-body">
|
||||||
<script src="/assets/js/generalFunctions.js"></script>
|
<script src="/assets/js/generalFunctions.js"></script>
|
||||||
<script src="/assets/js/navBarFunctions.js"></script>
|
|
||||||
<script src="/assets/js/assetConstructor.js"></script>
|
<script src="/assets/js/assetConstructor.js"></script>
|
||||||
{{> navBar}}
|
|
||||||
{{{ body }}}
|
{{{ body }}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<div class="column column--5 column--med-10 align-content-top">
|
<div class="column column--5 column--med-10 align-content-top">
|
||||||
<div class="column column--8 column--med-10">
|
<div class="column column--8 column--med-10">
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<h3>No Channel</h3>
|
<h3>No Channel</h3>
|
||||||
<p>There are no published channels matching your url</p>
|
<p>There are no published channels matching your url</p>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<h3>No Claims</h3>
|
<h3>No Claims</h3>
|
||||||
<p>There are no free assets at that claim. You should publish one at <a class="link--primary" href="/">spee.ch</a>.</p>
|
<p>There are no free assets at that claim. You should publish one at <a class="link--primary" href="/">spee.ch</a>.</p>
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="show-share-buttons">
|
<div id="show-share-buttons">
|
||||||
<div class="row row--padded row--wide">
|
<div class="row row--padded row--no-bottom row--wide">
|
||||||
<div class="column column--2 column--med-10">
|
<div class="column column--2 column--med-10">
|
||||||
<span class="text">Share:</span>
|
<span class="text">Share:</span>
|
||||||
</div><div class="column column--7 column--med-10">
|
</div><div class="column column--7 column--med-10">
|
||||||
|
@ -64,14 +64,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row row--wide">
|
<div id="show-details" class="row row--padded row--no-bottom row--wide" hidden="true">
|
||||||
<a class="text link--primary" id="show-details-toggle" href="#" onclick="toggleSection(event)" data-open="false" data-openlabel="[less]" data-closedlabel="[more]" data-slaveelementid="show-details">[more]</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="show-details" class="row row--padded row--wide" hidden="true">
|
|
||||||
<div id="show-claim-name">
|
<div id="show-claim-name">
|
||||||
<div class="column column--2 column--med-10">
|
<div class="column column--2 column--med-10">
|
||||||
<span class="text">Name:</span>
|
<span class="text">Claim Name:</span>
|
||||||
</div><div class="column column--8 column--med-10">
|
</div><div class="column column--8 column--med-10">
|
||||||
{{claimInfo.name}}
|
{{claimInfo.name}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,13 +79,6 @@
|
||||||
{{claimInfo.claimId}}
|
{{claimInfo.claimId}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="show-claim-id">
|
|
||||||
<div class="column column--2 column--med-10">
|
|
||||||
<span class="text">File Name:</span>
|
|
||||||
</div><div class="column column--8 column--med-10">
|
|
||||||
{{claimInfo.fileName}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="show-claim-id">
|
<div id="show-claim-id">
|
||||||
<div class="column column--2 column--med-10">
|
<div class="column column--2 column--med-10">
|
||||||
<span class="text">File Type:</span>
|
<span class="text">File Type:</span>
|
||||||
|
@ -102,3 +91,37 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row row--padded row--no-bottom row--wide">
|
||||||
|
<a class="text link--primary" id="show-details-toggle" href="#" onclick="toggleSection(event)" data-status="closed">[more]</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function toggleSection(event){
|
||||||
|
event.preventDefault();
|
||||||
|
var dataSet = event.target.dataset;
|
||||||
|
var status = dataSet.status;
|
||||||
|
var toggle = document.getElementById("show-details-toggle");
|
||||||
|
var details = document.getElementById("show-details");
|
||||||
|
if (status === "closed") {
|
||||||
|
details.hidden = false;
|
||||||
|
toggle.innerText = "[less]";
|
||||||
|
toggle.dataset.status = "open";
|
||||||
|
} else {
|
||||||
|
details.hidden = true;
|
||||||
|
toggle.innerText = "[more]";
|
||||||
|
toggle.dataset.status = "closed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function copyToClipboard(event){
|
||||||
|
var elementToCopy = event.target.dataset.elementtocopy;
|
||||||
|
var element = document.getElementById(elementToCopy);
|
||||||
|
var errorElement = 'input-error-copy-text' + elementToCopy;
|
||||||
|
element.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
} catch (err) {
|
||||||
|
validationFunctions.showError(errorElement, 'Oops, unable to copy');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
|
@ -29,9 +29,11 @@
|
||||||
|
|
||||||
<div id="channel-publish-in-progress" hidden="true">
|
<div id="channel-publish-in-progress" hidden="true">
|
||||||
<p>Creating your new channel. This may take a few seconds...</p>
|
<p>Creating your new channel. This may take a few seconds...</p>
|
||||||
<div id="create-channel-progress-bar"></div>
|
{{> progressBar}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="channel-publish-done" hidden="true">
|
<div id="channel-publish-done" hidden="true">
|
||||||
<p>Your channel has been successfully created!</p>
|
<p>Your channel has been successfully created!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/assets/js/createChannelFunctions.js"></script>
|
||||||
|
|
|
@ -24,3 +24,5 @@
|
||||||
<button class="button--primary" onclick="loginToChannel(event)">Authenticate</button>
|
<button class="button--primary" onclick="loginToChannel(event)">Authenticate</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<script src="/assets/js/loginFunctions.js"></script>
|
||||||
|
|
|
@ -24,15 +24,18 @@
|
||||||
<span class="nav-bar-tagline">Open-source, decentralized image and video sharing.</span>
|
<span class="nav-bar-tagline">Open-source, decentralized image and video sharing.</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-bar--right">
|
<div class="nav-bar--right">
|
||||||
<a class="nav-bar-link link--nav" href="/">Upload</a>
|
<a class="nav-bar-link link--nav" href="/">Publish</a>
|
||||||
<a class="nav-bar-link link--nav" href="/popular">Popular</a>
|
<!--<a class="nav-bar-link link--nav" href="/popular">Popular</a>-->
|
||||||
<a class="nav-bar-link link--nav" href="/about">About</a>
|
<a class="nav-bar-link link--nav" href="/about">About</a>
|
||||||
<select type="text" id="nav-bar-channel-select" class="select select--arrow link--nav" onchange="toggleNavBarSelection(event.target.selectedOptions[0].value)" {{#unless user}}style="display:none"{{/unless}}>
|
{{#if user }}
|
||||||
<option id="nav-bar-channel-select-channel-option">@{{user.userName}}</option>
|
<select type="text" id="nav-bar-channel-select" class="select select--arrow link--nav" onchange="toggleNavBarSelection(event)">
|
||||||
<option value="VIEW">View</option>
|
<option id="nav-bar-channel-select-channel-option" >{{user.channelName}}</option>
|
||||||
|
<option value="VIEW" data-channelUrl="/{{user.channelName}}:{{user.channelClaimId}}">View</option>
|
||||||
<option value="LOGOUT">Logout</option>
|
<option value="LOGOUT">Logout</option>
|
||||||
</select>
|
</select>
|
||||||
<a id="nav-bar-login-link" class="nav-bar-link link--nav" href="/login" {{#if user}}style="display:none"{{/if}}>Channel</a>
|
{{else}}
|
||||||
|
<a id="nav-bar-login-link" class="nav-bar-link link--nav" href="/login">Channel</a>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,4 +51,18 @@
|
||||||
link.setAttribute('class', 'select select--arrow link--nav-active');
|
link.setAttribute('class', 'select select--arrow link--nav-active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// function to send user to their channel if selected
|
||||||
|
function toggleNavBarSelection (event) {
|
||||||
|
console.log('toggleNavBarSelection event', event);
|
||||||
|
const selectedOption = event.target.selectedOptions[0].value;
|
||||||
|
if (selectedOption === 'LOGOUT') {
|
||||||
|
// send logout request to server
|
||||||
|
window.location.href = '/logout';
|
||||||
|
} else if (selectedOption === 'VIEW') {
|
||||||
|
// redirect to channel page
|
||||||
|
const channelUrl = event.target.selectedOptions[0].dataset.channelurl;
|
||||||
|
console.log('url:', channelUrl);
|
||||||
|
window.location.href = channelUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
|
@ -1,105 +0,0 @@
|
||||||
<!-- select whether to publish anonymously or in a channel -->
|
|
||||||
<div class="row row--padded row--short row--wide">
|
|
||||||
<div class="column column--10">
|
|
||||||
<form>
|
|
||||||
<div class="column column--3 column--med-10">
|
|
||||||
<input type="radio" name="anonymous-or-channel" id="anonymous-radio" class="input-radio" value="anonymous" {{#unless user}}checked {{/unless}} onchange="toggleChannel(event.target.value)"/>
|
|
||||||
<label class="label label--pointer" for="anonymous-radio">Anonymous</label>
|
|
||||||
</div><div class="column column--7 column--med-10">
|
|
||||||
<input type="radio" name="anonymous-or-channel" id="channel-radio" class="input-radio" value="in a channel" {{#if user}}checked {{/if}} onchange="toggleChannel(event.target.value)"/>
|
|
||||||
<label class="label label--pointer" for="channel-radio">In a channel</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="channel-select-options" {{#unless user}}hidden="true"{{/unless}}>
|
|
||||||
<div class="row row--padded row--no-top row--no-bottom row--wide">
|
|
||||||
<!--error display-->
|
|
||||||
<p id="input-error-channel-select" class="info-message-placeholder info-message--failure"></p>
|
|
||||||
<!--channel login/create select-->
|
|
||||||
<div class="column column--3">
|
|
||||||
<label class="label" for="channel-name-select">Channel:</label>
|
|
||||||
</div><div class="column column--7">
|
|
||||||
<select type="text" id="channel-name-select" class="select select--arrow" onchange="toggleSelectedChannel(event.target.selectedOptions[0].value)">
|
|
||||||
{{#if user}}
|
|
||||||
<option value="{{user.channelName}}" id="publish-channel-select-channel-option">{{user.channelName}}</option>
|
|
||||||
{{/if}}
|
|
||||||
<option value="login">Existing</option>
|
|
||||||
<option value="new" >New</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- log into an existing channel -->
|
|
||||||
<div id="channel-login-details" class="row row--padded row--short row--wide" {{#if user}}hidden="true"{{/if}}>
|
|
||||||
{{> channelLoginForm}}
|
|
||||||
</div>
|
|
||||||
<!-- create a channel -->
|
|
||||||
<div id="channel-create-details" class="row row--padded row--short row--wide" hidden="true">
|
|
||||||
{{> channelCreationForm}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
// show or hide the channel selection tools
|
|
||||||
function toggleChannel (selectedOption) {
|
|
||||||
const channelSelectOptions = document.getElementById('channel-select-options');
|
|
||||||
// show/hide the login and new channel forms
|
|
||||||
if (selectedOption === 'anonymous') {
|
|
||||||
channelSelectOptions.hidden = true;
|
|
||||||
channelSelectOptions.hidden = true;
|
|
||||||
// update url
|
|
||||||
updateUrl(selectedOption);
|
|
||||||
} else if (selectedOption === 'in a channel') {
|
|
||||||
channelSelectOptions.hidden = false;
|
|
||||||
// update url
|
|
||||||
let selectedChannel = document.getElementById('channel-name-select').selectedOptions[0].value
|
|
||||||
toggleSelectedChannel(selectedChannel);
|
|
||||||
} else {
|
|
||||||
console.log('selected option was not recognized');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// show or hide the channel create/login tool
|
|
||||||
function toggleSelectedChannel (selectedChannel) {
|
|
||||||
const createChannelTool = document.getElementById('channel-create-details');
|
|
||||||
const loginToChannelTool = document.getElementById('channel-login-details');
|
|
||||||
// show/hide the login and new channel forms
|
|
||||||
if (selectedChannel === 'new') {
|
|
||||||
createChannelTool.hidden = false;
|
|
||||||
loginToChannelTool.hidden = true;
|
|
||||||
} else if (selectedChannel === 'login') {
|
|
||||||
loginToChannelTool.hidden = false;
|
|
||||||
createChannelTool.hidden = true;
|
|
||||||
} else {
|
|
||||||
// hide the login and new channel forms
|
|
||||||
loginToChannelTool.hidden = true;
|
|
||||||
createChannelTool.hidden = true;
|
|
||||||
validationFunctions.hideError(document.getElementById('input-error-channel-select'));
|
|
||||||
}
|
|
||||||
// update url
|
|
||||||
updateUrl(selectedChannel);
|
|
||||||
}
|
|
||||||
function 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,50 +0,0 @@
|
||||||
<div class="row row--padded row--no-top row--no-bottom row--wide">
|
|
||||||
<div class="column column--10">
|
|
||||||
<a class="label link--primary" id="publish-details-toggle" href="#" onclick="toggleSection(event)" data-open="false" data-openlabel="[less]" data-closedlabel="[more]" data-slaveelementid="publish-details">[more]</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="publish-details" hidden="true" class="row row--padded row--wide">
|
|
||||||
|
|
||||||
<!-- description input -->
|
|
||||||
<div class="row row--no-top">
|
|
||||||
<div class="column column--3 column--med-10 align-content-top">
|
|
||||||
<label for="publish-license" class="label">Description:</label>
|
|
||||||
</div><div class="column column--7 column--sml-10">
|
|
||||||
<textarea rows="1" id="publish-description" class="textarea textarea--primary textarea--full-width" placeholder="Optional description"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row row--no-top">
|
|
||||||
<div class="column column--3 column--med-10">
|
|
||||||
<label for="publish-license" class="label">License:</label>
|
|
||||||
</div><div class="column column--7 column--sml-10">
|
|
||||||
<select type="text" id="publish-license" class="select select--primary">
|
|
||||||
<option value=" ">Unspecified</option>
|
|
||||||
<option value="Public Domain">Public Domain</option>
|
|
||||||
<option value="Creative Commons">Creative Commons</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row row--no-top">
|
|
||||||
<div class="column column--3">
|
|
||||||
<label for="publish-nsfw" class="label">Mature:</label>
|
|
||||||
</div><div class="column column--7">
|
|
||||||
<input class="input-checkbox" type="checkbox" id="publish-nsfw">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
const textarea = document.getElementById('publish-description');
|
|
||||||
const limit = 200;
|
|
||||||
textarea.oninput = () => {
|
|
||||||
textarea.style.height = '';
|
|
||||||
textarea.style.height = Math.min(textarea.scrollHeight, limit) + 'px';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<div class="row row--padded row--wide">
|
|
||||||
<div class="input-error" id="input-error-publish-submit" hidden="true"></div>
|
|
||||||
<button id="publish-submit" class="button--primary button--large" onclick="publishFileFunctions.publishStagedFile(event)">Upload</button>
|
|
||||||
</div>
|
|
||||||
<div class="row row--short align-content-center">
|
|
||||||
<button class="button--cancel" onclick="publishFileFunctions.cancelPublish()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
<div class="row row--short align-content-center">
|
|
||||||
<p class="fine-print">By clicking 'Upload', you affirm that you have the rights to publish this content to the LBRY network, and that you understand the properties of publishing it to a decentralized, user-controlled network. <a class="link--primary" target="_blank" href="https://lbry.io/learn">Read more.</a></p>
|
|
||||||
</div>
|
|
|
@ -1,56 +0,0 @@
|
||||||
<div class="row row--padded row--wide row--no-top" id="publish-thumbnail" hidden="true">
|
|
||||||
<div class="column column--3 column--sml-10">
|
|
||||||
<label class="label">Thumbnail:</label>
|
|
||||||
</div><div class="column column--6 column--sml-10">
|
|
||||||
<div class="input-text--primary">
|
|
||||||
<input type="text" id="claim-thumbnail-input" class="input-text input-text--full-width" placeholder="https://spee.ch/xyz/example.jpg" value="" oninput="updateVideoThumb(event)">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
function urlIsAnImage(url) {
|
|
||||||
return(url.match(/\.(jpeg|jpg|gif|png)$/) != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testImage(url, timeoutT) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
var timeout = timeoutT || 5000;
|
|
||||||
var timer, img = new Image();
|
|
||||||
img.onerror = img.onabort = function () {
|
|
||||||
clearTimeout(timer);
|
|
||||||
reject("error");
|
|
||||||
};
|
|
||||||
img.onload = function () {
|
|
||||||
clearTimeout(timer);
|
|
||||||
resolve("success");
|
|
||||||
};
|
|
||||||
timer = setTimeout(function () {
|
|
||||||
// reset .src to invalid URL so it stops previous
|
|
||||||
// loading, but doesn't trigger new load
|
|
||||||
img.src = "//!!!!/test.jpg";
|
|
||||||
reject("timeout");
|
|
||||||
}, timeout);
|
|
||||||
img.src = url;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateVideoThumb(event){
|
|
||||||
var videoPreview = document.getElementById('asset-preview');
|
|
||||||
var imageUrl = event.target.value;
|
|
||||||
if (urlIsAnImage(imageUrl)){
|
|
||||||
testImage(imageUrl, 3000)
|
|
||||||
.then(function(result) {
|
|
||||||
if (result === 'success'){
|
|
||||||
videoPreview.src = imageUrl;
|
|
||||||
} else if (result === 'timeout') {
|
|
||||||
console.log('could not resolve the provided thumbnail image url');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function(error) {
|
|
||||||
console.log('encountered an error loading thumbnail image url.')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -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>
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
{{#each trendingAssets}}
|
{{#each trendingAssets}}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--padded">
|
<div class="row row--padded">
|
||||||
<h3>Error</h3>
|
<h3>Error</h3>
|
||||||
<p>Unfortnately, Spee.ch encountered an error. You can help us out, by reporting the below error message in the #speech channel on <a class="link--primary" href="https://discord.gg/YjYbwhS" target="_blank">LBRY Discord</a>!</p>
|
<p>Unfortnately, Spee.ch encountered an error. You can help us out, by reporting the below error message in the #speech channel on <a class="link--primary" href="https://discord.gg/YjYbwhS" target="_blank">LBRY Discord</a>!</p>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{> navBar}}
|
||||||
<div class="row row--tall row--padded">
|
<div class="row row--tall row--padded">
|
||||||
<div class="column column--10">
|
<div class="column column--10">
|
||||||
<!-- title -->
|
<!-- title -->
|
||||||
|
|
32
webpack.config.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
const Path = require('path');
|
||||||
|
|
||||||
|
const REACT_ROOT = Path.resolve(__dirname, 'react/');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry : ['whatwg-fetch', './react/app.js'],
|
||||||
|
output: {
|
||||||
|
path : Path.join(__dirname, '/public/bundle/'),
|
||||||
|
filename: 'bundle.js',
|
||||||
|
},
|
||||||
|
watch : true,
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
test : /.jsx?$/,
|
||||||
|
loader : 'babel-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
query : {
|
||||||
|
presets: ['es2015', 'react', 'stage-2'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
REACT_ROOT,
|
||||||
|
'node_modules',
|
||||||
|
__dirname,
|
||||||
|
],
|
||||||
|
extensions: ['.js', '.jsx', '.scss'],
|
||||||
|
},
|
||||||
|
};
|
Forms are one of the most painful things in React. It might be worth looking into 3rd party React form elements. I've done a fair bit of work with forms and think that Formik is really nice. Hoping to add it to more parts of the app soon. https://github.com/jaredpalmer/formik
I really like how they use the
render
prop, I think it's a really nice way to interact with React elements