From 68faec1ae820be7de2e1a64ae64e0b1f225e3d23 Mon Sep 17 00:00:00 2001 From: Dessa Simpson Date: Sun, 1 Nov 2020 13:07:16 -0700 Subject: [PATCH] Add streamer UI Closes #11 --- db/30-votes.sql | 4 +- db/40-scores.sql | 2 +- public/main.js | 135 +++++++++++++++++++++++++++++++++++++++++++---- public/style.css | 15 +++++- src/app.ts | 3 ++ src/queries.ts | 7 ++- views/main.eta | 57 +++++++++++++++++++- 7 files changed, 207 insertions(+), 16 deletions(-) diff --git a/db/30-votes.sql b/db/30-votes.sql index 26c083d..0b878ba 100644 --- a/db/30-votes.sql +++ b/db/30-votes.sql @@ -2,6 +2,6 @@ CREATE TABLE votes ( requestUrl varchar, userId int, PRIMARY KEY (requestUrl,userId), - FOREIGN KEY (requestUrl) REFERENCES requests(url), - FOREIGN KEY (userId) REFERENCES users(userId) + FOREIGN KEY (requestUrl) REFERENCES requests(url) ON DELETE CASCADE, + FOREIGN KEY (userId) REFERENCES users(userId) ON DELETE CASCADE ); diff --git a/db/40-scores.sql b/db/40-scores.sql index 95a4c1d..8d23b81 100644 --- a/db/40-scores.sql +++ b/db/40-scores.sql @@ -3,5 +3,5 @@ CREATE TABLE scores ( score int NOT NULL DEFAULT 0, scoreModifier int NOT NULL DEFAULT 0, PRIMARY KEY (url), - FOREIGN KEY (url) REFERENCES requests(url) + FOREIGN KEY (url) REFERENCES requests(url) ON DELETE CASCADE ); diff --git a/public/main.js b/public/main.js index 6dbb63c..7dfed8a 100644 --- a/public/main.js +++ b/public/main.js @@ -10,26 +10,34 @@ function getRequests(count,allRequests) { reqUrl += `?count=${count}`; fetch(reqUrl) .then(response => response.json()) - .then(requests => buildTable(requests,allRequests)); + .then(requests => { + window.requests = requests; + console.log(requests); + buildTable(); + }); } -function buildTable(requests,allRequests) { +function buildTable() { var requestsDivHTML = ''; - if (allRequests) requestsDivHTML += `' requestsDivHTML += ""; for (request of requests) { console.log(request); requestsDivHTML += `\ \ `; - if (allRequests) requestsDivHTML += ``; + requestsDivHTML += ``; if (window.loggedIn) { if (request.voted) { requestsDivHTML += ``; } else { requestsDivHTML += ``; } + if (window.isStreamer) { + requestsDivHTML += '' + } } requestsDivHTML += ""; } @@ -38,9 +46,8 @@ function buildTable(requests,allRequests) { } function updateTable() { - var count = document.getElementById("count").value; - var allRequests = document.getElementById("allRequests").checked; - getRequests(count,allRequests); + allRequests = document.getElementById("allRequests").checked; + getRequests(document.getElementById("count").value,allRequests); } function addRequestErr(msg) { @@ -53,8 +60,21 @@ function addRequestErrReset() { document.getElementById('addRequestError').innerText = ""; } +function updateRequestErr(msg) { + document.getElementById('updateRequestError').style.display = "inline-block"; + document.getElementById('updateRequestError').innerText = msg; +} + +function updateRequestErrReset() { + document.getElementById('updateRequestError').style.display = "none"; + document.getElementById('updateRequestError').innerText = ""; +} + function showMessage(msg) { document.getElementById("messageModalText").innerText = msg; + document.getElementById("addRequestModal").style.display = "none"; + document.getElementById("updateRequestModal").style.display = "none"; + document.getElementById("deleteRequestModal").style.display = "none"; document.getElementById("modalBackground").style.display = "flex"; document.getElementById("messageModal").style.display = "block"; } @@ -66,6 +86,8 @@ function closeMessageModal() { function openAddRequestModal() { document.getElementById("modalBackground").style.display = "flex"; + document.getElementById("updateRequestModal").style.display = "none"; + document.getElementById("messageModal").style.display = "none"; document.getElementById("addRequestModal").style.display = "block"; } @@ -74,6 +96,49 @@ function closeAddRequestModal() { document.getElementById("addRequestModal").style.display = "none"; } +function openUpdateRequestModal(tr) { + var url = tr.getElementsByClassName('request-url')[0].innerText; + var score = tr.getElementsByClassName('request-score')[0].innerText; + var state = tr.getElementsByClassName('request-state')[0].innerText; + document.getElementById("updateRequestUrl").href = url; + document.getElementById("updateRequestUrl").innerText = url; + document.getElementById("updateRequestModalCurrentScore").innerText = score; + document.querySelector(`#updateRequestStateSelect [value="${state}"]`).selected = true; + document.getElementById("scoreModifierInput").value = 0; + document.getElementById("messageModal").style.display = "none"; + document.getElementById("addRequestModal").style.display = "none"; + document.getElementById("modalBackground").style.display = "flex"; + document.getElementById("updateRequestModal").style.display = "block"; +} + +function closeUpdateRequestModal() { + document.getElementById("modalBackground").style.display = "none"; + document.getElementById("updateRequestModal").style.display = "none"; +} + +function openDeleteRequestModal(url) { + document.getElementById("updateRequestUrl").href = url; + document.getElementById("updateRequestUrl").innerText = url; + document.getElementById("messageModal").style.display = "none"; + document.getElementById("addRequestModal").style.display = "none"; + document.getElementById("updateRequestModal").style.display = "none"; + document.getElementById("modalBackground").style.display = "flex"; + document.getElementById("deleteRequestModal").style.display = "block"; +} + +function closeDeleteRequestModal() { + document.getElementById("deleteRequestModal").style.display = "none"; + document.getElementById("updateRequestModal").style.display = "block"; +} + +function closeAllModals() { + document.getElementById("messageModal").style.display = "none"; + document.getElementById("addRequestModal").style.display = "none"; + document.getElementById("updateRequestModal").style.display = "none"; + document.getElementById("deleteRequestModal").style.display = "none"; + document.getElementById("modalBackground").style.display = "none"; +} + const validUrlRegexes = [ /^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/ ]; @@ -132,6 +197,50 @@ function deleteVote(url) { }); } +function updateRequestState(url,state) { + updateRequestErrReset(); + fetch("/api/updateRequestState", { method: 'POST', body: new URLSearchParams({ + url: url, + state: state + })}) + .then(response => { + if (!response.ok) { + response.text().then(updateRequestErr); + return; + } + updateTable(); + response.text().then(showMessage); + }); +} + +function updateRequestScoreModifier(url,scoreDiff) { + updateRequestErrReset(); + fetch("/api/updateRequestScoreModifier", { method: 'POST', body: new URLSearchParams({ + url: url, + scoreDiff: scoreDiff + })}) + .then(response => { + if (!response.ok) { + response.text().then(updateRequestErr); + return; + } + updateTable(); + response.text().then(showMessage); + }); +} + +function deleteRequest(url) { + fetch("/api/deleteRequest", { method: 'POST', body: new URLSearchParams({ + url: url + })}) + .then(response => { + updateTable(); + response.text().then(showMessage); + }); +} + + + updateTable(); document.addEventListener("keydown", function onEvent(event) { @@ -140,4 +249,12 @@ document.addEventListener("keydown", function onEvent(event) { closeAddRequestModal(); } }); -document.getElementById("modalBackground").addEventListener("click", (e) => { if (e.target === e.currentTarget) closeAddRequestModal();}); +document.getElementById("modalBackground").addEventListener("click", (e) => { if (e.target === e.currentTarget) closeAllModals();}); + +var updateRequestStateSelect = document.getElementById("updateRequestStateSelect"); +for(state of validStates) { + var opt = document.createElement("option"); + opt.text = state; + opt.value = state; + updateRequestStateSelect.add(opt) +} diff --git a/public/style.css b/public/style.css index 19b98da..75c6299 100644 --- a/public/style.css +++ b/public/style.css @@ -130,7 +130,7 @@ div#nav-userpic { box-shadow: 0px 5px 20px black; } -.modal h1 { +.modal h1, .modal h2, .modal h3 { margin: 0; } @@ -166,3 +166,16 @@ div#nav-userpic { margin-top: -3px; margin-bottom: -7px; } + +#scoreModifierInput { + width: 4em; + text-align: right; +} + +#scoreModifierHelp { + font-size: 75%; +} + +#deleteRequestLink { + color: #f00; +} diff --git a/src/app.ts b/src/app.ts index f7ab392..1363e8d 100644 --- a/src/app.ts +++ b/src/app.ts @@ -274,6 +274,7 @@ app.get("/callback", async (request, response) => { app.get("/", async (request, response) => { if (request.session) await validateApiToken(request.session); var streamerInfo = await db.query(queries.getStreamerInfo).then((result: pg.QueryResult) => result.rows[0]); + var validStates = JSON.stringify((await db.query(queries.getValidStates).then((result: pg.QueryResult) => result.rows)).map((row: any) => row.state)); if (typeof streamerInfo == 'undefined') response.redirect(307, `https://id.twitch.tv/oauth2/authorize?client_id=${config.twitchClientId}&redirect_uri=${config.urlPrefix}/callback&response_type=code&scope=channel:read:subscriptions moderation:read`); if (!request.session || !request.session.user) { response.render('main.eta', { @@ -288,6 +289,8 @@ app.get("/", async (request, response) => { loggedIn: true, userName: request.session.user.display_name, userProfilePicture: request.session.user.profile_image_url, + validStates: validStates, + isStreamer: streamerInfo['userid'] == request.session.user.id, streamerName: streamerInfo['displayname'], streamerProfilePicture: streamerInfo['imageurl'] }); diff --git a/src/queries.ts b/src/queries.ts index eb2d952..e7970aa 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -23,7 +23,7 @@ export const getStreamerIdToken = { export const getStreamerInfo = { name: "getStreamerInfo", - text: "SELECT displayname,imageurl FROM streamer_user_vw" + text: "SELECT userid,displayname,imageurl FROM streamer_user_vw" } export const updateStreamer = { @@ -91,3 +91,8 @@ export const checkVoteExists = { name: "checkVoteExists", text: "SELECT userid FROM votes WHERE requesturl = $1 AND userid = $2" } + +export const getValidStates = { + name: "getValidStates", + text: "SELECT * FROM states" +} diff --git a/views/main.eta b/views/main.eta index 11ef202..fb4afce 100644 --- a/views/main.eta +++ b/views/main.eta @@ -3,7 +3,11 @@ <%= it.streamerName %>'s Learn Request Queue - + @@ -32,7 +36,7 @@ View requests in any state -
+
+ +
URLRequesterScoreState`; - if (window.loggedIn) requestsDivHTML += `Vote`; + requestsDivHTML += 'State'; + if (window.loggedIn) requestsDivHTML += 'Vote'; + if (window.isStreamer) requestsDivHTML += 'Update
${request.url}${request.imageurl ? `` : ''}${request.requester}${request.score}${request.state}${request.state}