diff --git a/assets/js/pagination.js b/assets/js/pagination.js new file mode 100644 index 00000000..2e560a34 --- /dev/null +++ b/assets/js/pagination.js @@ -0,0 +1,93 @@ +'use strict'; + +const CURRENT_CONTINUATION = (new URL(document.location)).searchParams.get("continuation"); +const CONT_CACHE_KEY = `continuation_cache_${encodeURIComponent(window.location.pathname)}`; + +function get_data(){ + return JSON.parse(sessionStorage.getItem(CONT_CACHE_KEY)) || []; +} + +function save_data(){ + const prev_data = get_data(); + prev_data.push(CURRENT_CONTINUATION); + + sessionStorage.setItem(CONT_CACHE_KEY, JSON.stringify(prev_data)); +} + +function button_press(){ + let prev_data = get_data(); + if (!prev_data.length) return null; + + // Sanity check. Nowhere should the current continuation token exist in the cache + // but it can happen when using the browser's back feature. As such we'd need to travel + // back to the point where the current continuation token first appears in order to + // account for the rewind. + const conflict_at = prev_data.indexOf(CURRENT_CONTINUATION); + if (conflict_at != -1) { + prev_data.length = conflict_at; + } + + const prev_ctoken = prev_data.pop(); + + // On the first page, the stored continuation token is null. + if (prev_ctoken === null) { + sessionStorage.removeItem(CONT_CACHE_KEY); + let url = set_continuation(); + window.location.href = url; + + return; + } + + sessionStorage.setItem(CONT_CACHE_KEY, JSON.stringify(prev_data)); + let url = set_continuation(prev_ctoken); + + window.location.href = url; +}; + +// Method to set the current page's continuation token +// Removes the continuation parameter when a continuation token is not given +function set_continuation(prev_ctoken = null){ + let url = window.location.href.split('?')[0]; + let params = window.location.href.split('?')[1]; + let url_params = new URLSearchParams(params); + + if (prev_ctoken) { + url_params.set("continuation", prev_ctoken); + } else { + url_params.delete('continuation'); + }; + + if(Array.from(url_params).length > 0){ + return `${url}?${url_params.toString()}`; + } else { + return url; + } +} + +addEventListener('DOMContentLoaded', function(){ + const pagination_data = JSON.parse(document.getElementById('pagination-data').textContent); + const next_page_containers = document.getElementsByClassName("page-next-container"); + + for (let container of next_page_containers){ + const next_page_button = container.getElementsByClassName("pure-button") + + // exists? + if (next_page_button.length > 0){ + next_page_button[0].addEventListener("click", save_data); + } + } + + // Only add previous page buttons when not on the first page + if (CURRENT_CONTINUATION) { + const prev_page_containers = document.getElementsByClassName("page-prev-container") + + for (let container of prev_page_containers) { + if (pagination_data.is_rtl) { + container.innerHTML = `` + } else { + container.innerHTML = `` + } + container.getElementsByClassName("pure-button")[0].addEventListener("click", button_press); + } + } +}); diff --git a/locales/de.json b/locales/de.json index 46327f57..c9f55fb2 100644 --- a/locales/de.json +++ b/locales/de.json @@ -11,6 +11,7 @@ "last": "neueste", "Next page": "Nächste Seite", "Previous page": "Vorherige Seite", + "First page": "Erste Seite", "Clear watch history?": "Verlauf löschen?", "New password": "Neues Passwort", "New passwords must match": "Neue Passwörter müssen übereinstimmen", diff --git a/locales/en-US.json b/locales/en-US.json index 3987f796..35b7ec1b 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -33,6 +33,7 @@ "last": "last", "Next page": "Next page", "Previous page": "Previous page", + "First page": "First page", "Clear watch history?": "Clear watch history?", "New password": "New password", "New passwords must match": "New passwords must match", diff --git a/locales/ru.json b/locales/ru.json index 61bf9e92..67f09029 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -11,6 +11,7 @@ "last": "последние", "Next page": "Следующая страница", "Previous page": "Предыдущая страница", + "First page": "Первая страница", "Clear watch history?": "Очистить историю просмотров?", "New password": "Новый пароль", "New passwords must match": "Новые пароли не совпадают", diff --git a/src/invidious/frontend/pagination.cr b/src/invidious/frontend/pagination.cr index 3f931f4e..a29f5936 100644 --- a/src/invidious/frontend/pagination.cr +++ b/src/invidious/frontend/pagination.cr @@ -3,6 +3,24 @@ require "uri" module Invidious::Frontend::Pagination extend self + private def first_page(str : String::Builder, locale : String?, url : String) + str << %() + + if locale_is_rtl?(locale) + # Inverted arrow ("first" points to the right) + str << translate(locale, "First page") + str << "  " + str << %() + else + # Regular arrow ("first" points to the left) + str << %() + str << "  " + str << translate(locale, "First page") + end + + str << "" + end + private def previous_page(str : String::Builder, locale : String?, url : String) # Link str << %() @@ -72,18 +90,24 @@ module Invidious::Frontend::Pagination end end - def nav_ctoken(locale : String?, *, base_url : String | URI, ctoken : String?) + def nav_ctoken(locale : String?, *, base_url : String | URI, ctoken : String?, first_page : Bool, params : URI::Params) return String.build do |str| str << %(
\n) str << %(