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 = '
URL Requester Score ';
- if (allRequests) requestsDivHTML += `State`;
- if (window.loggedIn) requestsDivHTML += ` Vote`;
+ requestsDivHTML += ' State';
+ if (window.loggedIn) requestsDivHTML += ' Vote';
+ if (window.isStreamer) requestsDivHTML += ' Update '
requestsDivHTML += " ";
for (request of requests) {
console.log(request);
requestsDivHTML += `${request.url} \
${request.imageurl ? ` ` : ''}${request.requester} \
${request.score} `;
- if (allRequests) requestsDivHTML += `${request.state} `;
+ requestsDivHTML += `${request.state} `;
if (window.loggedIn) {
if (request.voted) {
requestsDivHTML += `Unvote `;
} else {
requestsDivHTML += `Vote `;
}
+ if (window.isStreamer) {
+ requestsDivHTML += 'Update '
+ }
}
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
-
+
@@ -47,6 +51,55 @@
Currently, only Youtube links are accepted.
+
+
+
Update Request
+
+
+
+
+ Current Score:
+
+
+ State:
+ Submit
+ )">
+
+
+
+ Modify Score:
+
+ Submit
+
+
+ Enter a number to add to (or negative to subtract from) the score of
+ a request. Use this for things like donations and channel points
+ redemptions.
+
+
+
+
+
+
+
+
Delete Request
+
WARNING:
+ Deleting a request will remove the request and all votes for it from the
+ database. This action is irreversible. It will NOT prevent the request
+ from being made again - use the Rejected state for that. Are you sure
+ you want to delete this request?
+
+
No
+
Yes
+