Authentication #170
17 changed files with 208 additions and 156 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
node_modules
|
node_modules
|
||||||
.idea
|
.idea
|
||||||
|
config/config.json
|
|
@ -8,7 +8,7 @@ module.exports = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let publishResults = {};
|
let publishResults = {};
|
||||||
// 1. make sure the name is available
|
// 1. make sure the name is available
|
||||||
publishHelpers.checkNameAvailability(publishParams.name)
|
publishHelpers.checkClaimNameAvailability(publishParams.name)
|
||||||
// 2. publish the file
|
// 2. publish the file
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
|
|
|
@ -100,7 +100,7 @@ module.exports = {
|
||||||
logger.debug(`successfully deleted ${filePath}`);
|
logger.debug(`successfully deleted ${filePath}`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
checkNameAvailability (name) {
|
checkClaimNameAvailability (name) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// find any records where the name is used
|
// find any records where the name is used
|
||||||
db.File.findAll({ where: { name } })
|
db.File.findAll({ where: { name } })
|
||||||
|
@ -133,27 +133,13 @@ module.exports = {
|
||||||
checkChannelAvailability (name) {
|
checkChannelAvailability (name) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// find any records where the name is used
|
// find any records where the name is used
|
||||||
db.Certificate.findAll({ where: { name } })
|
db.User.findAll({ where: { channelName: name } })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
|
logger.debug('sequelize result:', result);
|
||||||
if (result.length >= 1) {
|
if (result.length >= 1) {
|
||||||
// filter out any results that were not published from a spee.ch wallet address
|
return resolve(false);
|
||||||
getWalletList()
|
|
||||||
.then((walletList) => {
|
|
||||||
const filteredResult = result.filter((claim) => {
|
|
||||||
return walletList.includes(claim.address);
|
|
||||||
});
|
|
||||||
if (filteredResult.length >= 1) {
|
|
||||||
resolve(false);
|
|
||||||
} else {
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve(true);
|
|
||||||
}
|
}
|
||||||
|
resolve(true);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
reject(error);
|
reject(error);
|
||||||
|
|
20
migrations/Add-Address-To-User.js
Normal file
20
migrations/Add-Address-To-User.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface, Sequelize) => {
|
||||||
|
// logic for transforming into the new state
|
||||||
|
return queryInterface.addColumn(
|
||||||
|
'User',
|
||||||
|
'Address',
|
||||||
|
{
|
||||||
|
type : Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
down: (queryInterface, Sequelize) => {
|
||||||
|
// logic for reverting the changes
|
||||||
|
return queryInterface.removeColumn(
|
||||||
|
'User',
|
||||||
|
'Address'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
20
migrations/Add-UserId-To-Certificate.js
Normal file
20
migrations/Add-UserId-To-Certificate.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface, Sequelize) => {
|
||||||
|
// logic for transforming into the new state
|
||||||
|
queryInterface.addColumn(
|
||||||
|
'Certificate',
|
||||||
|
'UserId',
|
||||||
|
{
|
||||||
|
type : Sequelize.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
down: (queryInterface, Sequelize) => {
|
||||||
|
// logic for reverting the changes
|
||||||
|
queryInterface.removeColumn(
|
||||||
|
'Certificate',
|
||||||
|
'UserId'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
20
migrations/Add-UserId-To-File.js
Normal file
20
migrations/Add-UserId-To-File.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface, Sequelize) => {
|
||||||
|
// logic for transforming into the new state
|
||||||
|
return queryInterface.addColumn(
|
||||||
|
'File',
|
||||||
|
'UserId',
|
||||||
|
{
|
||||||
|
type : Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
down: (queryInterface, Sequelize) => {
|
||||||
|
// logic for reverting the changes
|
||||||
|
return queryInterface.removeColumn(
|
||||||
|
'File',
|
||||||
|
'UserId'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
|
@ -18,6 +18,10 @@ module.exports = (sequelize, { STRING }) => {
|
||||||
type : STRING,
|
type : STRING,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
address: {
|
||||||
|
type : STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
freezeTableName: true,
|
freezeTableName: true,
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"sequelize": "^4.1.0",
|
"sequelize": "^4.1.0",
|
||||||
|
"sequelize-cli": "^3.0.0-3",
|
||||||
"sleep": "^5.1.1",
|
"sleep": "^5.1.1",
|
||||||
"socket.io": "^2.0.1",
|
"socket.io": "^2.0.1",
|
||||||
"socketio-file-upload": "^0.6.0",
|
"socketio-file-upload": "^0.6.0",
|
||||||
|
|
|
@ -54,16 +54,7 @@
|
||||||
width: 90%
|
width: 90%
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-text--primary {
|
|
||||||
border: 0px;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-bottom: 1px solid grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-text--primary:focus {
|
|
||||||
outline: none;
|
|
||||||
border-bottom: 1px solid blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* show routes */
|
/* show routes */
|
||||||
.show-asset {
|
.show-asset {
|
||||||
|
|
|
@ -123,12 +123,36 @@ table {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-error {
|
/* BEM */
|
||||||
|
|
||||||
|
.info-message {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: red;
|
|
||||||
font-size: small;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-message--success {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-message--failure {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-text {
|
||||||
|
outline: none;
|
||||||
|
border: 0px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
|
.input-text--primary {
|
||||||
|
border-bottom: 1px solid grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-text--primary:focus {
|
||||||
|
border-bottom: 1px solid blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MEDIA QUERIES */
|
||||||
|
|
||||||
@media (max-width: 1250px) {
|
@media (max-width: 1250px) {
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
|
|
@ -55,18 +55,6 @@ function dataURItoBlob(dataURI) {
|
||||||
return new Blob([ia], {type:mimeString});
|
return new Blob([ia], {type:mimeString});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(elementId, errorMsg) {
|
|
||||||
var errorDisplay = document.getElementById(elementId);
|
|
||||||
errorDisplay.hidden = false;
|
|
||||||
errorDisplay.innerText = errorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearError(elementId) {
|
|
||||||
var errorDisplay = document.getElementById(elementId);
|
|
||||||
errorDisplay.hidden = true;
|
|
||||||
errorDisplay.innerText = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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';
|
||||||
|
|
|
@ -27,56 +27,6 @@ function validateFile(file) {
|
||||||
throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently 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 not already claimed
|
|
||||||
function isChannelAvailable (name) {
|
|
||||||
return new Promise(function(resolve, reject) {
|
|
||||||
// make sure the claim name is still available
|
|
||||||
var xhttp;
|
|
||||||
xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.open('GET', '/api/isChannelAvailable/' + name, true);
|
|
||||||
xhttp.responseType = 'json';
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 ) {
|
|
||||||
if ( this.status == 200) {
|
|
||||||
if (this.response == true) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject( new NameError("That name has already been claimed by another user. Please choose a different name."));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reject("request to check claim name failed with status:" + this.status);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.send();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation function that checks to make sure the claim name is not already claimed
|
|
||||||
function isNameAvailable (name) {
|
|
||||||
return new Promise(function(resolve, reject) {
|
|
||||||
// make sure the claim name is still available
|
|
||||||
var xhttp;
|
|
||||||
xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.open('GET', '/api/isClaimAvailable/' + name, true);
|
|
||||||
xhttp.responseType = 'json';
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 ) {
|
|
||||||
if ( this.status == 200) {
|
|
||||||
if (this.response == true) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject( new NameError("That name has already been claimed by another user. Please choose a different name."));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reject("request to check claim name failed with status:" + this.status);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.send();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation function that checks to make sure the claim name is valid
|
// validation function that checks to make sure the claim name is valid
|
||||||
function validateClaimName (name) {
|
function validateClaimName (name) {
|
||||||
// ensure a name was entered
|
// ensure a name was entered
|
||||||
|
@ -95,51 +45,85 @@ function cleanseClaimName(name) {
|
||||||
name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-'
|
name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-'
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
// validaiton function to check claim name as the input changes
|
|
||||||
function checkClaimName(name){
|
// validation functions to check claim & channel name eligibility as the inputs change
|
||||||
try {
|
|
||||||
// check to make sure the characters are valid
|
function isNameAvailable (name, apiUrl) {
|
||||||
validateClaimName(name);
|
return new Promise(function(resolve, reject) {
|
||||||
clearError('input-error-claim-name');
|
// make sure the claim name is still available
|
||||||
// check to make sure it is availabe
|
var xhttp;
|
||||||
isNameAvailable(name)
|
xhttp = new XMLHttpRequest();
|
||||||
.then(function() {
|
xhttp.open('GET', apiUrl + name, true);
|
||||||
document.getElementById('claim-name-available').hidden = false;
|
xhttp.responseType = 'json';
|
||||||
})
|
xhttp.onreadystatechange = function() {
|
||||||
.catch(function(error) {
|
if (this.readyState == 4 ) {
|
||||||
document.getElementById('claim-name-available').hidden = true;
|
if ( this.status == 200) {
|
||||||
showError('input-error-claim-name', error.message);
|
if (this.response == true) {
|
||||||
});
|
resolve();
|
||||||
} catch (error) {
|
} else {
|
||||||
showError('input-error-claim-name', error.message);
|
reject( new NameError("That name has already been claimed by another user. Please choose a different name."));
|
||||||
document.getElementById('claim-name-available').hidden = true;
|
}
|
||||||
}
|
} else {
|
||||||
|
reject("request to check claim name failed with status:" + this.status);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhttp.send();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showError(errorDisplay, errorMsg) {
|
||||||
|
errorDisplay.hidden = false;
|
||||||
|
errorDisplay.innerText = errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
// validaiton function to check claim name as the input changes
|
function hideError(errorDisplay) {
|
||||||
function checkChannelName(event){
|
errorDisplay.hidden = true;
|
||||||
console.log(event);
|
errorDisplay.innerText = '';
|
||||||
const name = event.target.value;
|
}
|
||||||
const target = document.getElementById(event.target.id);
|
|
||||||
const errorDisplay = target.parentNode.firstChild;
|
function showSuccess (successElement) {
|
||||||
console.log('error display:', errorDisplay)
|
successElement.hidden = false;
|
||||||
|
successElement.innerHTML = "✔";
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSuccess (successElement) {
|
||||||
|
successElement.hidden = true;
|
||||||
|
successElement.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, apiUrl) {
|
||||||
try {
|
try {
|
||||||
|
// check to make sure the characters are valid
|
||||||
|
validateClaimName(name);
|
||||||
// check to make sure it is available
|
// check to make sure it is available
|
||||||
isChannelAvailable(name)
|
isNameAvailable(name, apiUrl)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
errorDisplay.hidden = false;
|
hideError(errorDisplayElement);
|
||||||
|
showSuccess(successDisplayElement)
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
errorDisplay.hidden = false;
|
hideSuccess(successDisplayElement);
|
||||||
showError(errorDisplay.getAttribute('id'), error.message);
|
showError(errorDisplayElement, error.message);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error.message);
|
hideSuccess(successDisplayElement);
|
||||||
document.getElementById(errorDisplay.getAttribute('id')).hidden = true;
|
showError(errorDisplayElement, error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkClaimName(name){
|
||||||
|
const successDisplayElement = document.getElementById('claim-name-success');
|
||||||
|
const errorDisplayElement = document.getElementById('claim-name-error');
|
||||||
|
checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isClaimAvailable/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkChannelName(name){
|
||||||
|
const successDisplayElement = document.getElementById('channel-name-success');
|
||||||
|
const errorDisplayElement = document.getElementById('channel-name-error');
|
||||||
|
checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isChannelAvailable/');
|
||||||
|
}
|
||||||
|
|
||||||
// validation function which checks all aspects of the publish submission
|
// validation function which checks all aspects of the publish submission
|
||||||
function validateSubmission(stagedFiles, name){
|
function validateSubmission(stagedFiles, name){
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ const multipart = require('connect-multiparty');
|
||||||
const multipartMiddleware = multipart();
|
const multipartMiddleware = multipart();
|
||||||
const { publish } = require('../controllers/publishController.js');
|
const { publish } = require('../controllers/publishController.js');
|
||||||
const { getClaimList, resolveUri } = require('../helpers/lbryApi.js');
|
const { getClaimList, resolveUri } = require('../helpers/lbryApi.js');
|
||||||
const { createPublishParams, validateFile, checkNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
|
const { createPublishParams, validateFile, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
|
||||||
const errorHandlers = require('../helpers/errorHandlers.js');
|
const errorHandlers = require('../helpers/errorHandlers.js');
|
||||||
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ module.exports = (app, hostedContentPath) => {
|
||||||
// route to check whether spee.ch has published to a claim
|
// route to check whether spee.ch has published to a claim
|
||||||
app.get('/api/isClaimAvailable/:name', ({ ip, originalUrl, params }, res) => {
|
app.get('/api/isClaimAvailable/:name', ({ ip, originalUrl, params }, res) => {
|
||||||
// send response
|
// send response
|
||||||
checkNameAvailability(params.name)
|
checkClaimNameAvailability(params.name)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
res.status(200).json(true);
|
res.status(200).json(true);
|
||||||
|
@ -39,8 +39,7 @@ module.exports = (app, hostedContentPath) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// route to check whether spee.ch has published to a channel
|
// route to check whether spee.ch has published to a channel
|
||||||
app.get('/api/isChannelAvailable/:name', ({ ip, originalUrl, params }, res) => {
|
app.get('/api/isChannelAvailable/:name', ({ params }, res) => {
|
||||||
// send response
|
|
||||||
checkChannelAvailability(params.name)
|
checkChannelAvailability(params.name)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
|
@ -51,6 +50,7 @@ module.exports = (app, hostedContentPath) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
logger.debug('api/isChannelAvailable/ error', error);
|
||||||
res.status(500).json(error);
|
res.status(500).json(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
<form id="login-form" action="/login" method="post">
|
<form id="login-form" action="/login" method="post">
|
||||||
<div>
|
<div>
|
||||||
<label>Username:</label>
|
<label>Username:</label>
|
||||||
@ <input type="text" name="username" class="input-text--primary"/>
|
@ <input type="text" name="username" class="input-text input-text--primary"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Password:</label>
|
<label>Password:</label>
|
||||||
<input type="password" name="password" class="input-text--primary"/>
|
<input type="password" name="password" class="input-text input-text--primary"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="submit" value="Log In"/>
|
<input type="submit" value="Log In"/>
|
||||||
|
@ -23,11 +23,11 @@
|
||||||
<form action="/signup" method="post">
|
<form action="/signup" method="post">
|
||||||
<div>
|
<div>
|
||||||
<label>Channel name:</label>
|
<label>Channel name:</label>
|
||||||
@ <input type="text" name="username" class="input-text--primary"/>
|
@ <input type="text" name="username" class="input-text input-text--primary"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Password:</label>
|
<label>Password:</label>
|
||||||
<input type="password" name="password" class="input-text--primary"/>
|
<input type="password" name="password" class="input-text input-text--primary"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="submit" value="Create"/>
|
<input type="submit" value="Create"/>
|
||||||
|
|
|
@ -1,21 +1,34 @@
|
||||||
<div id="claim-channel-input-area">
|
<div id="claim-channel-input-area">
|
||||||
<label for="publish-channel">Channel:</label></td>
|
|
||||||
<select type="text" id="publish-channel" name="channel" value="channel" onclick="check(event)">
|
<p>
|
||||||
{{#if user}}
|
<label for="publish-channel">Channel:</label></td>
|
||||||
<option value="@{{user.channelName}}" >@{{user.channelName}}</option>
|
<select type="text" id="publish-channel" name="channel" value="channel" onclick="check(event)">
|
||||||
{{/if}}
|
{{#if user}}
|
||||||
<option value="@speech" >Anonymous</option>
|
<option value="@{{user.channelName}}" >@{{user.channelName}}</option>
|
||||||
<option value="new" >New</option>
|
{{/if}}
|
||||||
</select>
|
<option value="@speech" >Anonymous</option>
|
||||||
<div id="channel-create-details" hidden="true"><p id="test" style="color: red;"></p>
|
<option value="new" >New</option>
|
||||||
<label for="channelName">Channel Name: </label>
|
</select>
|
||||||
@<input type="text" id="channel-name-input" class="input-text--primary" oninput="checkChannelName(event)">
|
</p>
|
||||||
<br/>
|
|
||||||
<label for="password" >Password: </label>
|
<div id="channel-create-details" hidden="true">
|
||||||
<input type="text" id="password" class="input-text--primary">
|
|
||||||
<br/>
|
<span id="channel-name-error" class="info-message info-message--failure"></span>
|
||||||
<button >create</button>
|
<p>
|
||||||
|
<label for="channelName">Channel Name: </label>
|
||||||
|
@<input type="text" id="channel-name-input" class="input-text input-text--primary" placeholder="exampleChannel" oninput="checkChannelName(event.target.value)">
|
||||||
|
<span id="channel-name-success" class="info-message info-message--success"></span>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<label for="password" >Password: </label>
|
||||||
|
<input type="password" id="password" placeholder="" class="input-text input-text--primary">
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button >create</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="publish-title">Title: </label></td>
|
<td><label for="publish-title">Title: </label></td>
|
||||||
<td><input type="text" id="publish-title" class="input-text--primary"></td>
|
<td><input type="text" id="publish-title" class="input-text input-text--primary"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="publish-description">Description: </label></td>
|
<td><label for="publish-description">Description: </label></td>
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-right">
|
<div class="col-right">
|
||||||
<div id="publish-active-area">
|
<div id="publish-active-area">
|
||||||
<div class="input-error" id="input-error-claim-name" hidden="true"></div>
|
|
||||||
<div class="stop-float">
|
<div class="stop-float">
|
||||||
|
<span id="claim-name-error" class="info-message info-message--failure" hidden="true"></span>
|
||||||
<p>
|
<p>
|
||||||
Spee.ch/
|
Spee.ch/
|
||||||
<input type="text" id="claim-name-input" class="input-text--primary" placeholder="your-url-here" oninput="checkClaimName(event.target.value)">
|
<input type="text" id="claim-name-input" class="input-text input-text--primary" placeholder="your-url-here" oninput="checkClaimName(event.target.value)">
|
||||||
<span id="claim-name-available" hidden="true" style="color: green">✔</span>
|
<span id="claim-name-success" class="info-message info-message--success"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="stop-float">
|
<div class="stop-float">
|
||||||
|
@ -55,6 +55,6 @@
|
||||||
document.getElementById('input-error-file-selection').innerHTML = '';
|
document.getElementById('input-error-file-selection').innerHTML = '';
|
||||||
document.getElementById('input-error-claim-name').innerHTML = '';
|
document.getElementById('input-error-claim-name').innerHTML = '';
|
||||||
document.getElementById('input-error-publish-submit').innerHTML = '';
|
document.getElementById('input-error-publish-submit').innerHTML = '';
|
||||||
document.getElementById('claim-name-available').hidden = true;
|
document.getElementById('claim-name-success').hidden = true;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in a new issue
What if I'm not on Chrome?