From dd651d5cacab409e901bb1e20b5cae9950689a57 Mon Sep 17 00:00:00 2001 From: towards-a-new-leftypol Date: Thu, 24 Aug 2023 18:07:37 -0400 Subject: [PATCH] Pagination for known spam works well enough --- index.html | 2 +- script.js | 225 +++++++++++++++++++++++++++++++++++++++++++++-------- style.css | 9 +++ 3 files changed, 202 insertions(+), 34 deletions(-) diff --git a/index.html b/index.html index 2e67d5a..23eae3c 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@
-

Hello World

+

Spam Noticer

diff --git a/script.js b/script.js index 927be6f..497bae5 100644 --- a/script.js +++ b/script.js @@ -108,7 +108,7 @@ function resolvePath(routes, path, query_params) { function resolve(path_data, route_part) { console.log('calling content'); - console.log(path_data, route_part); + console.log(path_data, route_part, query_params); route_part.content(path_data, query_params); } @@ -140,6 +140,165 @@ var ROUTES = RoutePart( , handlePageRoot ); +/** + * Pagination + */ + +function createPagination(start_page_num, url, name, page_size, renderContent) { + const numDisplayedPages = 5; // Specify the number of numbered links to display + + // Helper function to update the URL with the new page number + function updatePageNumberInURL(pageNum) { + let params; + + if (window.location.search == "") { + params = {} + } else { + console.log("calling parseQueryParams. window.location.search:", window.location.search); + params = parseQueryParams(window.location.search.substring(1)); + } + + console.log("updatePageNumberInURL", params); + params[name.replaceAll(' ', '_')] = pageNum; + changeUrl("/", params, true); + } + + function createPaginationControls(page_num, dataset_size) { + const container = div(); + container.classList.add('pagination'); + + // Calculate the total number of pages + const total_pages = Math.ceil(dataset_size / page_size); + console.log('page_num:', page_num, 'page_size:', page_size, 'dataset_size:', dataset_size, 'total_pages:', total_pages); + + function createNumberedLink(pageNum) { + const link = document.createElement('a'); + + link.addEventListener('click', function(e) { + //loadNthPage(url, pageNum - 1, page_size) + e.preventDefault(); + updatePageNumberInURL(pageNum - 1); + }); + + link.appendChild(text(pageNum)); + + if (pageNum - 1 === page_num) { + link.classList.add('active'); // Apply CSS class for active page + } else { + link.setAttribute('href', '#'); + } + + return link; + } + + // Add Previous link if applicable + if (page_num > 0) { + const prev_link = document.createElement('a'); + prev_link.setAttribute('href', '#'); + + prev_link.addEventListener('click', function(e) { + e.preventDefault(); + //loadNthPage(url, page_num - 1, page_size) + updatePageNumberInURL(page_num - 1); + }); + + prev_link.appendChild(text('Previous')); + container.appendChild(prev_link); + } + + // Add numbered links + const firstDisplayedPage = Math.max(1, page_num - Math.floor(numDisplayedPages / 2)); + const lastDisplayedPage = Math.min(total_pages, firstDisplayedPage + numDisplayedPages - 1); + + if (firstDisplayedPage > 1) { + container.appendChild(createNumberedLink(1)); + + if (firstDisplayedPage > 2) { + container.appendChild(text('...')); + } + } + + for (let i = firstDisplayedPage; i <= lastDisplayedPage; i++) { + container.appendChild(createNumberedLink(i)); + } + + if (lastDisplayedPage < total_pages) { + if (lastDisplayedPage < total_pages - 1) { + container.appendChild(text('...')); + } + + container.appendChild(createNumberedLink(total_pages)); + } + + // Add Next link if applicable + if (page_num < total_pages - 1) { + const next_link = document.createElement('a'); + next_link.setAttribute('href', '#'); + + next_link.addEventListener('click', function(e) { + //loadNthPage(url, page_num + 1, page_size) + e.preventDefault(); + updatePageNumberInURL(page_num + 1); + }); + + next_link.appendChild(text('Next')); + container.appendChild(next_link); + } + + return container; + } + + function loadNthPage(url, page_num) { + // Calculate the range of results based on the current page + const start = page_num * page_size; + const end = start + page_size - 1; + + var headers = { + 'Authorization': 'Bearer ' + GlobalVars.settings.jwt, + //'Prefer': 'count=estimated', + 'Prefer': 'count=exact', + 'Range-Unit': 'items', + 'Range': `${start}-${end}` + }; + + pageInfoText.innerText = `Loading page ${page_num} of ${name}`; + cancelGlobalEvts(); + + if (page_num > 0) { + } + + return get(url, cancel, headers) + .then(function(response) { + // Retrieve the range and total number of results from the HTTP headers + const rangeHeader = response.getResponseHeader('Content-Range'); + const [start, end, total_results] = parseRangeHeader(rangeHeader); + const json = JSON.parse(response.responseText); + const controlsA = createPaginationControls(page_num, total_results); + const controlsB = createPaginationControls(page_num, total_results); + renderContent(json, controlsA, controlsB); + pageInfoText.innerText = `${name} ${page_num + 1}/${Math.ceil(total_results / page_size)}`; + }) + .catch(caught.bind(this, `Failed to load ${url} page ${page_num}. Reason:`)); + } + + loadNthPage(url, start_page_num); +} + +// Helper function to parse the Content-Range header +function parseRangeHeader(rangeHeader) { + const match = rangeHeader.match(/(\d+)-(\d+)\/(\d+)/); + + if (match) { + const start = parseInt(match[1]); + const end = parseInt(match[2]); + const totalResults = parseInt(match[3]); + return [start, end, totalResults]; + } + + return [0, 0, 0]; // Default values if the header is not available or not in the expected format +} + + /** * Functions */ @@ -156,7 +315,7 @@ function resolveResponse(resolve, reject) { } } else { if (e.target.status >= 200 && e.target.status < 300) { - return resolve(e.target.responseText); + return resolve(e.target); } else { reject(new Error('API responded with ' + e.target.status)); } @@ -361,10 +520,6 @@ function keepElements(elem_selectors) { } } -function loadJwt() { - cancelGlobalEvts(); -} - function caught(m, e) { console.error(m); console.error(e); @@ -402,7 +557,8 @@ function mkSpamImgElement(img_description) { function renderNav() { var nav = document.createElement('nav'); var a_elem = _mkelem('a', text('All Known Spam Posts')); - a_elem.addEventListener('click', function() { + a_elem.addEventListener('click', function(e) { + e.preventDefault(); changeUrl('/', null, true); }); a_elem.setAttribute('href', '#'); @@ -476,7 +632,6 @@ function renderAttachmentReason(attachment, known_spam_post_attachments) { 'match': `${Math.round(distance * 100 * 10) / 10}%` }; - container.appendChild(renderThings(things)); return container; } @@ -556,34 +711,36 @@ function renderOverviewPost(post) { return postContainer; } -function loadKnownSpamPosts() { - pageInfoText.innerText = 'Loading all known spam posts...'; - cancelGlobalEvts(); +function loadKnownSpamPosts(params) { + if (params == null) { + params = {}; + } + const pageName = "known spam posts"; var url = GlobalVars.settings.postgrest_url + '/known_spam_post?select=*,known_spam_post_attachment(*,phash::text)' - var headers = { - 'Authorization': 'Bearer ' + GlobalVars.settings.jwt - }; - get(url, cancel, headers) - .then(function(response) { - var json = JSON.parse(response); - console.log("results for", url, response); - pageInfoText.innerText = 'Have these known spam posts:'; - var mainElem = document.querySelector('main'); - mainElem.innerHTML = ''; + const pageNum = Number(params[pageName.replaceAll(' ', '_')] || 0); - json.forEach(function (post) { - var postElem = renderOverviewPost(post); + function renderContent(json, controlsA, controlsB) { + pageInfoText.innerText = 'Have these known spam posts:'; + var mainElem = document.querySelector('main'); + mainElem.innerHTML = ''; + mainElem.appendChild(controlsA); - postElem.addEventListener('click', function() { - changeUrl('/spam_post/' + post.text_post_id, null, true); - }); + json.forEach(function (post) { + var postElem = renderOverviewPost(post); - mainElem.appendChild(postElem); + postElem.addEventListener('click', function() { + changeUrl('/spam_post/' + post.text_post_id, null, true); }); - }) - .catch(caught.bind(this, "Failed to load known spam posts. Reason:")); + + mainElem.appendChild(postElem); + }); + + mainElem.appendChild(controlsB); + } + + createPagination(pageNum, url, pageName, 25, renderContent); } function changeUrl(path, search_query, push_state) { @@ -602,7 +759,7 @@ function changeUrl(path, search_query, push_state) { function handlePageRoot(_, query_params) { console.log("handlePageRoot", JSON.stringify(query_params)); - return loadKnownSpamPosts(); + return loadKnownSpamPosts(query_params); } function showSpamPost(path_data, _) { @@ -638,7 +795,7 @@ function showSpamPost(path_data, _) { get(url, cancel, headers) .then(function(response) { - var json = JSON.parse(response); + var json = JSON.parse(response.responseText); console.log(json); pageInfoText.innerText = 'Known Spam Post'; @@ -657,7 +814,7 @@ function showSpamPost(path_data, _) { get(url, cancel, headers) .then(function(response) { - var json = JSON.parse(response); + var json = JSON.parse(response.responseText); console.log("known spam attachments:", json); knownSpamAttachmentsSectionElem .appendChild(renderKnownSpamAttachments(json)); @@ -706,8 +863,10 @@ function gatherElements() { } function parseQueryParams(querystring) { + console.log('parseQueryParams', querystring, querystring.split('&')); return querystring.split('&') .map(function(part) { + console.log(part); return part.split('=').map(decodeURIComponent); }) .reduce(function(acc, pair) { return { [pair[0]]: pair[1] } }, {}); @@ -739,7 +898,7 @@ function getSettings() { pageInfoText.innerText = 'Loading settings'; return get('/static/settings.json', cancel) .then(function(response) { - var result = JSON.parse(response); + var result = JSON.parse(response.responseText); console.log("settings response:", result); GlobalVars.settings = result; pageInfoText.innerText = 'Have settings.'; diff --git a/style.css b/style.css index 1b09b4b..71efca5 100644 --- a/style.css +++ b/style.css @@ -28,3 +28,12 @@ header { .attachment { display: flow-root; } + +.pagination a { + margin: 1em; + font-size: 2em; +} + +main nav { + margin: 1em; +}