Implement addRequest in UI

- addRequest modal
  - Error box inside addRequest modal
- Generic message modal
- URL validation - same code as server
- Update /api/addRequest to get requester from session
- Fix return of requests.addRequest() for already requested
- Make requests.addRequest() return 201 when created
master
Dessa Simpson 2020-07-06 21:24:04 -07:00
parent e54db3c4eb
commit bf89c6956d
5 changed files with 92 additions and 22 deletions

View File

@ -34,18 +34,75 @@ function updateTable() {
getRequests(count,allRequests); getRequests(count,allRequests);
} }
function addRequestErr(msg) {
document.getElementById('addRequestError').style.display = "inline-block";
document.getElementById('addRequestError').innerText = msg;
}
function addRequestErrReset() {
document.getElementById('addRequestError').style.display = "none";
document.getElementById('addRequestError').innerText = "";
}
function showMessage(msg) {
document.getElementById("messageModalText").innerText = msg;
document.getElementById("modalBackground").style.display = "flex";
document.getElementById("messageModal").style.display = "block";
}
function closeMessageModal() {
document.getElementById("modalBackground").style.display = "none";
document.getElementById("messageModal").style.display = "none";
}
function openAddRequestModal() { function openAddRequestModal() {
document.getElementById("addRequestModalBackground").style.display = "flex"; document.getElementById("modalBackground").style.display = "flex";
document.getElementById("addRequestModal").style.display = "block";
} }
function closeAddRequestModal() { function closeAddRequestModal() {
document.getElementById("addRequestModalBackground").style.display = "none"; document.getElementById("modalBackground").style.display = "none";
document.getElementById("addRequestModal").style.display = "none";
}
const validUrlRegexes = [
/^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/
];
function validateAndSubmitRequest() {
addRequestErrReset();
var url = document.getElementById("addRequestUrl").value;
var validUrl = false;
for (var regex of validUrlRegexes) {
if (regex.test(url)) {
validUrl = true;
break;
}
}
if (!validUrl) {
addRequestErr("Invalid URL");
return;
}
fetch("/api/addRequest", { method: 'POST', body: new URLSearchParams({
url: url
})})
.then(response => {
if (!response.ok) {
response.text().then(addRequestErr);
return;
}
closeAddRequestModal();
updateTable();
document.getElementById("addRequestUrl").value = "";
response.text().then(showMessage);
});
} }
updateTable(); updateTable();
document.addEventListener("keydown", function onEvent(event) { document.addEventListener("keydown", function onEvent(event) {
if (event.key === "Escape") { if (event.key === "Escape") {
closeMessageModal();
closeAddRequestModal(); closeAddRequestModal();
} }
}); });
document.getElementById("modalBackground").addEventListener("click", (e) => { if (e.target === e.currentTarget) closeAddRequestModal();});

View File

@ -14,6 +14,13 @@ a:hover {
color: #fff; color: #fff;
} }
.error {
padding: 0.25em 0.5em;
margin: 1em;
display: none;
background-color: #f00c;
}
#topbar { #topbar {
padding: 0.25em 0.5em; padding: 0.25em 0.5em;
position: absolute; position: absolute;
@ -84,9 +91,11 @@ div#nav-userpic {
text-align: right; text-align: right;
} }
#addRequestModalBackground { #modalBackground {
display: none; display: none;
position: fixed; position: fixed;
align-items: center;
justify-content: center;
z-index: 1; z-index: 1;
top: 0; top: 0;
left: 0; left: 0;
@ -94,11 +103,10 @@ div#nav-userpic {
height: 100%; height: 100%;
background-color: #444; background-color: #444;
background-color: #444a; background-color: #444a;
align-items: center;
justify-content: center;
} }
#addRequestModal { .modal {
display: none;
position: relative; position: relative;
padding: 1em; padding: 1em;
margin: 2em; margin: 2em;
@ -108,11 +116,12 @@ div#nav-userpic {
box-shadow: 0px 5px 20px black; box-shadow: 0px 5px 20px black;
} }
#addRequestModal h1 { .modal h1 {
margin-top: 0; margin: 0;
} }
#addRequestInputContainer { #addRequestInputContainer {
margin-top: 1em;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
@ -123,13 +132,13 @@ div#nav-userpic {
margin: 0 5px; margin: 0 5px;
} }
#addRequestModalClose { .modalClose {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0.5em; right: 0.5em;
font-size: 150%; font-size: 150%;
} }
#addRequestModalClose a { .modalClose a {
text-decoration: none; text-decoration: none;
} }

View File

@ -38,18 +38,18 @@ app.get("/api/getAllRequests", async (request, response) => {
app.post("/api/addRequest", async (request, response) => { app.post("/api/addRequest", async (request, response) => {
response.type('text/plain'); response.type('text/plain');
if (!request.session || !request.session.user) {
response.status(401);
response.send("Must be logged in");
return;
}
if (!request.body.url) { if (!request.body.url) {
response.status(400); response.status(400);
response.send("Missing url"); response.send("Missing url");
return return
} }
if (!request.body.requester) {
response.status(400);
response.send("Missing requester");
return
}
var url = request.body.url as string; var url = request.body.url as string;
var requester = request.body.requester as string; var requester = request.session.user.display_name;
requests.addRequest(url,requester).then((val: [number,string]) => { requests.addRequest(url,requester).then((val: [number,string]) => {
response.status(val[0]); response.status(val[0]);
response.send(val[1]); response.send(val[1]);

View File

@ -52,11 +52,11 @@ export async function addRequest(url: string, requester: string) {
var query = Object.assign(checkRequestExistsQuery, { values: [url] }); var query = Object.assign(checkRequestExistsQuery, { values: [url] });
var result = await db.query(query); var result = await db.query(query);
if (result.rowCount > 0) { if (result.rowCount > 0) {
return `Song already requested by ${result.rows[0].requester}. State: ${result.rows[0].state}` return [200,`Song already requested by ${result.rows[0].requester}. State: ${result.rows[0].state}`]
} }
var query = Object.assign(addRequestQuery, { values: [url,requester] }); var query = Object.assign(addRequestQuery, { values: [url,requester] });
return db.query(query) return db.query(query)
.then((result: pg.QueryResult) => [200,"Song request added."]); .then((result: pg.QueryResult) => [201,"Song request added."]);
}; };
// updateRequestState // updateRequestState

View File

@ -30,10 +30,15 @@
</select> </select>
<input type="checkbox" id="allRequests" onchange="updateTable()">View requests in any state</input> <input type="checkbox" id="allRequests" onchange="updateTable()">View requests in any state</input>
</div> </div>
<div id="addRequestModalBackground" onclick="closeAddRequestModal()"> <div id="modalBackground">
<div id="addRequestModal"> <div class="modal" id="messageModal">
<div id="addRequestModalClose"><a href="#" onclick="closeAddRequestModal()">&times;</a></div> <div class="modalClose"><a href="#" onclick="closeMessageModal()">&times;</a></div>
<span id="messageModalText"></span>
</div>
<div class="modal" id="addRequestModal">
<div class="modalClose"><a href="#" onclick="closeAddRequestModal()">&times;</a></div>
<h1>Add Request</h1> <h1>Add Request</h1>
<div class="error" id="addRequestError"></div>
<span id="addRequestInputContainer"> <span id="addRequestInputContainer">
URL: <input id="addRequestUrl" placeholder="https://www.youtube.com/watch?v=dQw4w9WgXcQ"></input> URL: <input id="addRequestUrl" placeholder="https://www.youtube.com/watch?v=dQw4w9WgXcQ"></input>
<button onclick="validateAndSubmitRequest()">Request</button><br> <button onclick="validateAndSubmitRequest()">Request</button><br>
@ -43,4 +48,3 @@
</div> </div>
</body> </body>
</html> </html>