Add pagination
Note: Requests API endpoints /getRequests and /getRequestsAll have breaking changes (the response schema changed entirely). Fixes #26master
parent
c634e763f3
commit
84a22ccffd
|
@ -1,29 +1,49 @@
|
||||||
var requestsDiv = document.getElementById("requests");
|
var requestsDiv = document.getElementById("requests");
|
||||||
var cronJobs = ['processBans']
|
var cronJobs = ['processBans'];
|
||||||
|
var currentPage = 1;
|
||||||
|
var totalPages = 1;
|
||||||
|
|
||||||
function getRequests(count,allRequests) {
|
function getRequests(count,offset,allRequests) {
|
||||||
var reqUrl;
|
if (allRequests) var reqUrl = "/api/getAllRequests";
|
||||||
if (allRequests) {
|
else var reqUrl = "/api/getRequests";
|
||||||
reqUrl = "/api/getAllRequests";
|
reqUrl += `?count=${count}&offset=${offset}`;
|
||||||
} else {
|
|
||||||
reqUrl = "/api/getRequests";
|
|
||||||
}
|
|
||||||
reqUrl += `?count=${count}`;
|
|
||||||
fetch(reqUrl)
|
fetch(reqUrl)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(requests => {
|
.then(requests => {
|
||||||
window.requests = requests;
|
buildTable(requests);
|
||||||
buildTable();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTable() {
|
function buildTable(requests) {
|
||||||
|
totalPages = Math.ceil(requests.total/document.getElementById("count").value);
|
||||||
|
document.getElementById("totalPages").innerText = totalPages;
|
||||||
|
if (currentPage <= 1) {
|
||||||
|
currentPage = 1;
|
||||||
|
document.getElementById("pageBtnFirst").disabled = true;
|
||||||
|
document.getElementById("pageBtnPrev").disabled = true;
|
||||||
|
} else {
|
||||||
|
document.getElementById("pageBtnFirst").disabled = false;
|
||||||
|
document.getElementById("pageBtnPrev").disabled = false;
|
||||||
|
}
|
||||||
|
if (currentPage >= totalPages) {
|
||||||
|
currentPage = totalPages;
|
||||||
|
document.getElementById("pageBtnLast").disabled = true;
|
||||||
|
document.getElementById("pageBtnNext").disabled = true;
|
||||||
|
} else {
|
||||||
|
document.getElementById("pageBtnLast").disabled = false;
|
||||||
|
document.getElementById("pageBtnNext").disabled = false;
|
||||||
|
}
|
||||||
|
document.getElementById("page").innerHTML = "";
|
||||||
|
for (i = 1; i <= totalPages; i++) {
|
||||||
|
document.getElementById("page").innerHTML += `<option value=${i}>${i}</option>`;
|
||||||
|
}
|
||||||
|
document.getElementById("page").value = currentPage;
|
||||||
var requestsDivHTML = '<table><tr><th class="request-link">Song</th><th class="request-requester">Requester</th><th class="request-score">Score</th>';
|
var requestsDivHTML = '<table><tr><th class="request-link">Song</th><th class="request-requester">Requester</th><th class="request-score">Score</th>';
|
||||||
requestsDivHTML += '<th class="request-state">State</td>';
|
requestsDivHTML += '<th class="request-state">State</td>';
|
||||||
if (window.loggedIn) requestsDivHTML += '<th class="request-vote">Vote</td>';
|
if (window.loggedIn) requestsDivHTML += '<th class="request-vote">Vote</td>';
|
||||||
if (window.isStreamer) requestsDivHTML += '<th class="request-update">Update</th>'
|
if (window.isStreamer) requestsDivHTML += '<th class="request-update">Update</th>'
|
||||||
requestsDivHTML += "</tr>";
|
requestsDivHTML += "</tr>";
|
||||||
for (request of requests) {
|
for (request of requests.requests) {
|
||||||
requestsDivHTML += `<tr><td class="request-link"><a href="${request.url}" target="_blank">${request.title}</a></td>\
|
requestsDivHTML += `<tr><td class="request-link"><a href="${request.url}" target="_blank">${request.title}</a></td>\
|
||||||
<td class="request-requester">${request.imageurl ? `<img src="${request.imageurl}" class="table-userpic"/>` : ''}${request.requester}</td>\
|
<td class="request-requester">${request.imageurl ? `<img src="${request.imageurl}" class="table-userpic"/>` : ''}${request.requester}</td>\
|
||||||
<td class="request-score">${request.score}</td>`;
|
<td class="request-score">${request.score}</td>`;
|
||||||
|
@ -45,8 +65,10 @@ function buildTable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTable() {
|
function updateTable() {
|
||||||
allRequests = document.getElementById("allRequests").checked;
|
var count = document.getElementById("count").value;
|
||||||
getRequests(document.getElementById("count").value,allRequests);
|
var offset = (currentPage - 1) * count;
|
||||||
|
var allRequests = document.getElementById("allRequests").checked;
|
||||||
|
getRequests(count,offset,allRequests);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyUrlTransforms(url) {
|
function applyUrlTransforms(url) {
|
||||||
|
@ -66,6 +88,11 @@ function applyUrlTransforms(url) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToPage(page) {
|
||||||
|
currentPage = parseInt(page,10);
|
||||||
|
updateTable();
|
||||||
|
}
|
||||||
|
|
||||||
function getColorObject() {
|
function getColorObject() {
|
||||||
return {
|
return {
|
||||||
bg: {
|
bg: {
|
||||||
|
|
|
@ -111,6 +111,11 @@ div#nav-userpic {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tableSettings {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
#modalBackground {
|
#modalBackground {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
43
src/app.ts
43
src/app.ts
|
@ -42,31 +42,47 @@ app.use(session({
|
||||||
|
|
||||||
// API
|
// API
|
||||||
app.get("/api/getRequests", async (request, response) => {
|
app.get("/api/getRequests", async (request, response) => {
|
||||||
if (!request.session) {
|
if (!request.session) throw new Error ("Missing request.session");
|
||||||
throw new Error ("Missing request.session")
|
|
||||||
}
|
|
||||||
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
|
||||||
await validateApiToken(request.session);
|
await validateApiToken(request.session);
|
||||||
|
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
||||||
|
var requestOffset = ( request.query.offset ? parseInt(request.query.offset as string, 10) : 0 );
|
||||||
|
var requestsTotal = await requests.getRequestsTotal();
|
||||||
if (request.session.user) {
|
if (request.session.user) {
|
||||||
requests.getRequestsVoted(requestCount,request.session.user.id).then((val: Array<any>) => response.send(val))
|
requests.getRequestsVoted(requestCount,requestOffset,request.session.user.id)
|
||||||
|
.then((val: Array<any>) => response.send({
|
||||||
|
total: requestsTotal,
|
||||||
|
requests: val
|
||||||
|
}))
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
} else {
|
} else {
|
||||||
requests.getRequests(requestCount).then((val: Array<any>) => response.send(val))
|
requests.getRequests(requestCount,requestOffset)
|
||||||
|
.then((val: Array<any>) => response.send({
|
||||||
|
total: requestsTotal,
|
||||||
|
requests: val
|
||||||
|
}))
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/getAllRequests", async (request, response) => {
|
app.get("/api/getAllRequests", async (request, response) => {
|
||||||
if (!request.session) {
|
if (!request.session) throw new Error ("Missing request.session");
|
||||||
throw new Error ("Missing request.session")
|
|
||||||
}
|
|
||||||
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
|
||||||
await validateApiToken(request.session);
|
await validateApiToken(request.session);
|
||||||
|
var requestCount = ( request.query.count ? parseInt(request.query.count as string, 10) : 5 );
|
||||||
|
var requestOffset = ( request.query.offset ? parseInt(request.query.offset as string, 10) : 0 );
|
||||||
|
var requestsTotal = await requests.getAllRequestsTotal();
|
||||||
if (request.session.user) {
|
if (request.session.user) {
|
||||||
requests.getAllRequestsVoted(requestCount,request.session.user.id).then((val: Array<any>) => response.send(val))
|
requests.getAllRequestsVoted(requestCount,requestOffset,request.session.user.id)
|
||||||
|
.then((val: Array<any>) => response.send({
|
||||||
|
total: requestsTotal,
|
||||||
|
requests: val
|
||||||
|
}))
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
} else {
|
} else {
|
||||||
requests.getAllRequests(requestCount).then((val: Array<any>) => response.send(val))
|
requests.getAllRequests(requestCount,requestOffset)
|
||||||
|
.then((val: Array<any>) => response.send({
|
||||||
|
total: requestsTotal,
|
||||||
|
requests: val
|
||||||
|
}))
|
||||||
.catch((e: any) => errorHandler(request,response,e));
|
.catch((e: any) => errorHandler(request,response,e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -79,7 +95,8 @@ app.post("/api/addRequest", async (request, response) => {
|
||||||
response.send("Session expired; please log in again");
|
response.send("Session expired; please log in again");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var banned = await db.query(Object.assign(queries.checkBan, { values: [request.session.user.id] })).then((result: pg.QueryResult) => result.rowCount > 0);
|
var banned = await db.query(Object.assign(queries.checkBan, { values: [request.session.user.id] }))
|
||||||
|
.then((result: pg.QueryResult) => result.rowCount > 0);
|
||||||
if (banned) {
|
if (banned) {
|
||||||
response.status(401);
|
response.status(401);
|
||||||
response.send("You are banned; you may not add new requests.");
|
response.send("You are banned; you may not add new requests.");
|
||||||
|
|
|
@ -56,27 +56,38 @@ export const updateVotePoints = {
|
||||||
export const getRequests = {
|
export const getRequests = {
|
||||||
name: "getRequests",
|
name: "getRequests",
|
||||||
text: "SELECT * FROM requests_vw \
|
text: "SELECT * FROM requests_vw \
|
||||||
JOIN states ON requests_vw.state = states.state \
|
JOIN states ON requests_vw.state = states.state WHERE active \
|
||||||
WHERE active ORDER BY score DESC, reqTimestamp ASC LIMIT $1"
|
ORDER BY score DESC, reqTimestamp ASC LIMIT $1 OFFSET $2"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRequestsVoted = {
|
export const getRequestsVoted = {
|
||||||
name: "getRequestsVoted",
|
name: "getRequestsVoted",
|
||||||
text: "SELECT * FROM get_requests_voted($2) \
|
text: "SELECT * FROM get_requests_voted($3) \
|
||||||
JOIN states ON get_requests_voted.state = states.state \
|
JOIN states ON get_requests_voted.state = states.state WHERE active \
|
||||||
WHERE active ORDER BY score DESC, reqTimestamp ASC LIMIT $1"
|
ORDER BY score DESC, reqTimestamp ASC LIMIT $1 OFFSET $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRequestsTotal = {
|
||||||
|
name: "getRequestsTotal",
|
||||||
|
text: "SELECT COUNT(*) FROM requests_vw \
|
||||||
|
JOIN states ON requests_vw.state = states.state WHERE active"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllRequests = {
|
export const getAllRequests = {
|
||||||
name: "getAllRequests",
|
name: "getAllRequests",
|
||||||
text: "SELECT * FROM requests_vw \
|
text: "SELECT * FROM requests_vw \
|
||||||
ORDER BY score DESC, reqTimestamp ASC LIMIT $1"
|
ORDER BY score DESC, reqTimestamp ASC LIMIT $1 OFFSET $2"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllRequestsVoted = {
|
export const getAllRequestsVoted = {
|
||||||
name: "getAllRequestsVoted",
|
name: "getAllRequestsVoted",
|
||||||
text: "SELECT * FROM get_requests_voted($2) \
|
text: "SELECT * FROM get_requests_voted($3) \
|
||||||
ORDER BY score DESC, reqTimestamp ASC LIMIT $1"
|
ORDER BY score DESC, reqTimestamp ASC LIMIT $1 OFFSET $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAllRequestsTotal = {
|
||||||
|
name: "getAllRequestsTotal",
|
||||||
|
text: "SELECT COUNT(*) FROM requests_vw"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkRequestExists = {
|
export const checkRequestExists = {
|
||||||
|
|
|
@ -4,30 +4,40 @@ import { log, LogLevel } from "./logging"
|
||||||
import pg from "pg";
|
import pg from "pg";
|
||||||
import db from "./db";
|
import db from "./db";
|
||||||
|
|
||||||
export async function getRequests(count: number) {
|
export async function getRequests(count: number, offset: number) {
|
||||||
var query = Object.assign(queries.getRequests, { values: [count] });
|
var query = Object.assign(queries.getRequests, { values: [count,offset] });
|
||||||
return db.query(query)
|
return db.query(query)
|
||||||
.then((result: pg.QueryResult) => result.rows);
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getAllRequests(count: number) {
|
export async function getRequestsVoted(count: number, offset: number, user: number) {
|
||||||
var query = Object.assign(queries.getAllRequests, { values: [count] });
|
var query = Object.assign(queries.getRequestsVoted, { values: [count,offset,user] });
|
||||||
return db.query(query)
|
return db.query(query)
|
||||||
.then((result: pg.QueryResult) => result.rows);
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getRequestsVoted(count: number, user: number) {
|
export async function getRequestsTotal() {
|
||||||
var query = Object.assign(queries.getRequestsVoted, { values: [count,user] });
|
return db.query(queries.getRequestsTotal)
|
||||||
|
.then((result: pg.QueryResult) => result.rows[0]["count"]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getAllRequests(count: number, offset: number) {
|
||||||
|
var query = Object.assign(queries.getAllRequests, { values: [count,offset] });
|
||||||
return db.query(query)
|
return db.query(query)
|
||||||
.then((result: pg.QueryResult) => result.rows);
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getAllRequestsVoted(count: number,user: number) {
|
export async function getAllRequestsVoted(count: number, offset: number, user: number) {
|
||||||
var query = Object.assign(queries.getAllRequestsVoted, { values: [count,user] });
|
var query = Object.assign(queries.getAllRequestsVoted, { values: [count,offset,user] });
|
||||||
return db.query(query)
|
return db.query(query)
|
||||||
.then((result: pg.QueryResult) => result.rows);
|
.then((result: pg.QueryResult) => result.rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function getAllRequestsTotal() {
|
||||||
|
return db.query(queries.getAllRequestsTotal)
|
||||||
|
.then((result: pg.QueryResult) => result.rows[0]["count"]);
|
||||||
|
};
|
||||||
|
|
||||||
const validUrlRegexes = [
|
const validUrlRegexes = [
|
||||||
/^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/
|
/^https:\/\/www\.youtube\.com\/watch\?v=[a-zA-Z0-9_-]{11}$/
|
||||||
];
|
];
|
||||||
|
|
|
@ -30,15 +30,31 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div id="requests"></div><br>
|
<div id="requests"></div><br>
|
||||||
Count:
|
<div id="tableSettings">
|
||||||
<select id="count" value="10" onchange="updateTable()">
|
<span style="width:420px">
|
||||||
<option>5</option>
|
Count:
|
||||||
<option selected="selected">10</option>
|
<select id="count" value="10" onchange="updateTable()">
|
||||||
<option>25</option>
|
<option>5</option>
|
||||||
<option>50</option>
|
<option selected="selected">10</option>
|
||||||
<option>100</option>
|
<option>25</option>
|
||||||
</select>
|
<option>50</option>
|
||||||
<input type="checkbox" id="allRequests" onchange="updateTable()">View requests in any state</input>
|
<option>100</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<button id="pageBtnFirst" onclick="goToPage(1)"><<</button>
|
||||||
|
<button id="pageBtnPrev" onclick="goToPage(currentPage-1)"><</button>
|
||||||
|
<select id="page" onchange="goToPage(this.value)">
|
||||||
|
<option value=1>1</option>
|
||||||
|
</select>
|
||||||
|
of <span id="totalPages">?</span>
|
||||||
|
<button id="pageBtnNext" onclick="goToPage(currentPage+1)">></button>
|
||||||
|
<button id="pageBtnLast" onclick="goToPage(totalPages)">>></button>
|
||||||
|
</span>
|
||||||
|
<span style="width:420px">
|
||||||
|
<input type="checkbox" id="allRequests" onchange="updateTable()">View learned and rejected requests</input>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="modalBackground">
|
<div id="modalBackground">
|
||||||
<div class="modal" id="messageModal">
|
<div class="modal" id="messageModal">
|
||||||
|
|
Loading…
Reference in New Issue