Add pagination

Note: Requests API endpoints /getRequests and /getRequestsAll have 
breaking changes (the response schema changed entirely).

Fixes #26
master
Dessa Simpson 2021-02-25 19:52:57 -07:00
parent c634e763f3
commit 84a22ccffd
6 changed files with 139 additions and 53 deletions

View File

@ -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: {

View File

@ -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;

View File

@ -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.");

View File

@ -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 = {

View File

@ -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}$/
]; ];

View File

@ -30,6 +30,8 @@
</div> </div>
<div id="main"> <div id="main">
<div id="requests"></div><br> <div id="requests"></div><br>
<div id="tableSettings">
<span style="width:420px">
Count: Count:
<select id="count" value="10" onchange="updateTable()"> <select id="count" value="10" onchange="updateTable()">
<option>5</option> <option>5</option>
@ -38,7 +40,21 @@
<option>50</option> <option>50</option>
<option>100</option> <option>100</option>
</select> </select>
<input type="checkbox" id="allRequests" onchange="updateTable()">View requests in any state</input> </span>
<span>
<button id="pageBtnFirst" onclick="goToPage(1)">&lt;&lt;</button>
<button id="pageBtnPrev" onclick="goToPage(currentPage-1)">&lt;</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)">&gt;</button>
<button id="pageBtnLast" onclick="goToPage(totalPages)">&gt;&gt;</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">