mirror of
https://github.com/iv-org/invidious.git
synced 2025-03-01 02:41:25 -05:00
Frontend: Add a first page and previous page buttons for channel navigation (#4123)
This commit is contained in:
commit
f95f87e448
93
assets/js/pagination.js
Normal file
93
assets/js/pagination.js
Normal file
@ -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 = `<button class="pure-button pure-button-secondary">${pagination_data.prev_page} <i class="icon ion-ios-arrow-forward"></i></button>`
|
||||||
|
} else {
|
||||||
|
container.innerHTML = `<button class="pure-button pure-button-secondary"><i class="icon ion-ios-arrow-back"></i> ${pagination_data.prev_page}</button>`
|
||||||
|
}
|
||||||
|
container.getElementsByClassName("pure-button")[0].addEventListener("click", button_press);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -11,6 +11,7 @@
|
|||||||
"last": "neueste",
|
"last": "neueste",
|
||||||
"Next page": "Nächste Seite",
|
"Next page": "Nächste Seite",
|
||||||
"Previous page": "Vorherige Seite",
|
"Previous page": "Vorherige Seite",
|
||||||
|
"First page": "Erste Seite",
|
||||||
"Clear watch history?": "Verlauf löschen?",
|
"Clear watch history?": "Verlauf löschen?",
|
||||||
"New password": "Neues Passwort",
|
"New password": "Neues Passwort",
|
||||||
"New passwords must match": "Neue Passwörter müssen übereinstimmen",
|
"New passwords must match": "Neue Passwörter müssen übereinstimmen",
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"last": "last",
|
"last": "last",
|
||||||
"Next page": "Next page",
|
"Next page": "Next page",
|
||||||
"Previous page": "Previous page",
|
"Previous page": "Previous page",
|
||||||
|
"First page": "First page",
|
||||||
"Clear watch history?": "Clear watch history?",
|
"Clear watch history?": "Clear watch history?",
|
||||||
"New password": "New password",
|
"New password": "New password",
|
||||||
"New passwords must match": "New passwords must match",
|
"New passwords must match": "New passwords must match",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"last": "последние",
|
"last": "последние",
|
||||||
"Next page": "Следующая страница",
|
"Next page": "Следующая страница",
|
||||||
"Previous page": "Предыдущая страница",
|
"Previous page": "Предыдущая страница",
|
||||||
|
"First page": "Первая страница",
|
||||||
"Clear watch history?": "Очистить историю просмотров?",
|
"Clear watch history?": "Очистить историю просмотров?",
|
||||||
"New password": "Новый пароль",
|
"New password": "Новый пароль",
|
||||||
"New passwords must match": "Новые пароли не совпадают",
|
"New passwords must match": "Новые пароли не совпадают",
|
||||||
|
@ -3,6 +3,24 @@ require "uri"
|
|||||||
module Invidious::Frontend::Pagination
|
module Invidious::Frontend::Pagination
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
|
private def first_page(str : String::Builder, locale : String?, url : String)
|
||||||
|
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
||||||
|
|
||||||
|
if locale_is_rtl?(locale)
|
||||||
|
# Inverted arrow ("first" points to the right)
|
||||||
|
str << translate(locale, "First page")
|
||||||
|
str << " "
|
||||||
|
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
||||||
|
else
|
||||||
|
# Regular arrow ("first" points to the left)
|
||||||
|
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
||||||
|
str << " "
|
||||||
|
str << translate(locale, "First page")
|
||||||
|
end
|
||||||
|
|
||||||
|
str << "</a>"
|
||||||
|
end
|
||||||
|
|
||||||
private def previous_page(str : String::Builder, locale : String?, url : String)
|
private def previous_page(str : String::Builder, locale : String?, url : String)
|
||||||
# Link
|
# Link
|
||||||
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
||||||
@ -72,18 +90,24 @@ module Invidious::Frontend::Pagination
|
|||||||
end
|
end
|
||||||
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|
|
return String.build do |str|
|
||||||
str << %(<div class="h-box">\n)
|
str << %(<div class="h-box">\n)
|
||||||
str << %(<div class="page-nav-container flexible">\n)
|
str << %(<div class="page-nav-container flexible">\n)
|
||||||
|
|
||||||
str << %(<div class="page-prev-container flex-left"></div>\n)
|
str << %(<div class="page-prev-container flex-left">)
|
||||||
|
|
||||||
|
if !first_page
|
||||||
|
self.first_page(str, locale, base_url.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
str << %(</div>\n)
|
||||||
|
|
||||||
str << %(<div class="page-next-container flex-right">)
|
str << %(<div class="page-next-container flex-right">)
|
||||||
|
|
||||||
if !ctoken.nil?
|
if !ctoken.nil?
|
||||||
params_next = URI::Params{"continuation" => ctoken}
|
params["continuation"] = ctoken
|
||||||
url_next = HttpServer::Utils.add_params_to_url(base_url, params_next)
|
url_next = HttpServer::Utils.add_params_to_url(base_url, params)
|
||||||
|
|
||||||
self.next_page(str, locale, url_next.to_s)
|
self.next_page(str, locale, url_next.to_s)
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
page_nav_html = IV::Frontend::Pagination.nav_ctoken(locale,
|
page_nav_html = IV::Frontend::Pagination.nav_ctoken(locale,
|
||||||
base_url: relative_url,
|
base_url: relative_url,
|
||||||
ctoken: next_continuation
|
ctoken: next_continuation,
|
||||||
|
first_page: continuation.nil?,
|
||||||
|
params: env.params.query,
|
||||||
)
|
)
|
||||||
%>
|
%>
|
||||||
|
|
||||||
@ -40,6 +42,8 @@
|
|||||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/channel/<%= ucid %>" />
|
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/channel/<%= ucid %>" />
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
|
|
||||||
|
<script src="/js/pagination.js?v=<%= ASSET_COMMIT %>"></script>
|
||||||
|
|
||||||
<link rel="alternate" href="<%= youtube_url %>">
|
<link rel="alternate" href="<%= youtube_url %>">
|
||||||
<title><%= author %> - Invidious</title>
|
<title><%= author %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -8,4 +8,14 @@
|
|||||||
|
|
||||||
<%= page_nav_html %>
|
<%= page_nav_html %>
|
||||||
|
|
||||||
|
<script id="pagination-data" type="application/json">
|
||||||
|
<%=
|
||||||
|
{
|
||||||
|
"next_page" => translate(locale, "Next page"),
|
||||||
|
"prev_page" => translate(locale, "Previous page"),
|
||||||
|
"is_rtl" => locale_is_rtl?(locale)
|
||||||
|
}.to_pretty_json
|
||||||
|
%>
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="/js/watched_indicator.js"></script>
|
<script src="/js/watched_indicator.js"></script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user