add: ability to enable pages instead of infinite scrolling

This commit is contained in:
trisua 2025-06-19 22:10:17 -04:00
parent fa72d6a59d
commit ffdf320c14
15 changed files with 47 additions and 15 deletions

View file

@ -44,9 +44,10 @@
("ui_ident" "io_data_load") ("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker")))) (div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script (script
(text "setTimeout(() => { (text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?user_id={{ profile.id }}&tag={{ tag }}&page=\", Number.parseInt(\"{{ page }}\") - 1]); trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?user_id={{ profile.id }}&tag={{ tag }}&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
});")) });"))
(text "{% endblock %}") (text "{% endblock %}")

View file

@ -1403,6 +1403,11 @@
\"Hides dislikes on all posts. Users will also no longer be able to dislike your posts.\", \"Hides dislikes on all posts. Users will also no longer be able to dislike your posts.\",
\"text\", \"text\",
], ],
[
[\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
\"{{ profile.settings.paged_timelines }}\",
\"checkbox\",
],
[[], \"Fun\", \"title\"], [[], \"Fun\", \"title\"],
[ [
[\"disable_gpa_fun\", \"Disable GPA\"], [\"disable_gpa_fun\", \"Disable GPA\"],

View file

@ -83,9 +83,10 @@
("ui_ident" "io_data_load") ("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))) (div ("ui_ident" "io_data_marker")))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script (script
(text "setTimeout(() => { (text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?stack_id={{ stack.id }}&page=\", Number.parseInt(\"{{ page }}\") - 1]); trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?stack_id={{ stack.id }}&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
});")) });"))
(text "{%- endif %}")))) (text "{%- endif %}"))))

View file

@ -33,9 +33,10 @@
("ui_ident" "io_data_load") ("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker")))) (div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script (script
(text "setTimeout(() => { (text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=AllPosts&page=\", Number.parseInt(\"{{ page }}\") - 1]); trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=AllPosts&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
});")) });"))
(text "{% endblock %}") (text "{% endblock %}")

View file

@ -11,9 +11,10 @@
("ui_ident" "io_data_load") ("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker")))) (div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script (script
(text "setTimeout(() => { (text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=FollowingPosts&page=\", Number.parseInt(\"{{ page }}\") - 1]); trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=FollowingPosts&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
});")) });"))
(text "{% endblock %}") (text "{% endblock %}")

View file

@ -31,9 +31,10 @@
(div ("ui_ident" "io_data_marker"))) (div ("ui_ident" "io_data_marker")))
(text "{%- endif %}")) (text "{%- endif %}"))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script (script
(text "setTimeout(() => { (text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=MyCommunities&page=\", Number.parseInt(\"{{ page }}\") - 1]); trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=MyCommunities&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
});")) });"))
(text "{% endblock %}") (text "{% endblock %}")

View file

@ -11,9 +11,10 @@
("ui_ident" "io_data_load") ("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker")))) (div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script (script
(text "setTimeout(() => { (text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=PopularPosts&page=\", Number.parseInt(\"{{ page }}\") - 1]); trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=PopularPosts&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
});")) });"))
(text "{% endblock %}") (text "{% endblock %}")

View file

@ -30,3 +30,7 @@
(str (text "chats:label.go_back"))) (str (text "chats:label.go_back")))
(text "{%- endif %}")) (text "{%- endif %}"))
(text "{%- endif %}") (text "{%- endif %}")
(text "{% if paginated -%}")
(text "{{ components::pagination(page=page, items=list|length) }}")
(text "{%- endif %}")

View file

@ -1141,7 +1141,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
}, },
); );
self.define("io_data_load", (_, tmpl, page) => { self.define("io_data_load", (_, tmpl, page, paginated_mode = false) => {
self.IO_DATA_MARKER = document.querySelector( self.IO_DATA_MARKER = document.querySelector(
"[ui_ident=io_data_marker]", "[ui_ident=io_data_marker]",
); );
@ -1164,7 +1164,16 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
self.IO_DATA_PAGE = page; self.IO_DATA_PAGE = page;
self.IO_DATA_SEEN_IDS = []; self.IO_DATA_SEEN_IDS = [];
self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER); if (!paginated_mode) {
self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER);
} else {
// immediately load first page
self.IO_DATA_TMPL = self.IO_DATA_TMPL.replace("&page=", "");
self.IO_DATA_TMPL += `&paginated=true&page=`;
self.io_load_data();
}
self.IO_PAGINATED = paginated_mode;
}); });
self.define("io_load_data", async () => { self.define("io_load_data", async () => {

View file

@ -213,7 +213,7 @@ pub async fn upload_avatar_request(
if mime == "image/gif" { if mime == "image/gif" {
// gif image, don't encode // gif image, don't encode
if img.0.len() > MAXIMUM_GIF_FILE_SIZE { if img.0.len() > MAXIMUM_GIF_FILE_SIZE {
return Json(Error::DataTooLong("gif".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
std::fs::write(&path, img.0).unwrap(); std::fs::write(&path, img.0).unwrap();
@ -226,7 +226,7 @@ pub async fn upload_avatar_request(
// check file size // check file size
if img.0.len() > MAXIMUM_FILE_SIZE { if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::DataTooLong("image".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
// upload image // upload image
@ -314,7 +314,7 @@ pub async fn upload_banner_request(
if mime == "image/gif" { if mime == "image/gif" {
// gif image, don't encode // gif image, don't encode
if img.0.len() > MAXIMUM_GIF_FILE_SIZE { if img.0.len() > MAXIMUM_GIF_FILE_SIZE {
return Json(Error::DataTooLong("gif".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
std::fs::write(&path, img.0).unwrap(); std::fs::write(&path, img.0).unwrap();
@ -327,7 +327,7 @@ pub async fn upload_banner_request(
// check file size // check file size
if img.0.len() > MAXIMUM_FILE_SIZE { if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::DataTooLong("image".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
// upload image // upload image

View file

@ -136,7 +136,7 @@ pub async fn upload_avatar_request(
// check file size // check file size
if img.0.len() > MAXIMUM_FILE_SIZE { if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::DataTooLong("image".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
// upload image // upload image
@ -191,7 +191,7 @@ pub async fn upload_banner_request(
// check file size // check file size
if img.0.len() > MAXIMUM_FILE_SIZE { if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::DataTooLong("image".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
// upload image // upload image

View file

@ -133,7 +133,7 @@ pub async fn create_request(
// check sizes // check sizes
for img in &images { for img in &images {
if img.len() > MAXIMUM_FILE_SIZE { if img.len() > MAXIMUM_FILE_SIZE {
return Json(Error::DataTooLong("image".to_string()).into()); return Json(Error::FileTooLarge.into());
} }
} }

View file

@ -576,6 +576,8 @@ pub struct TimelineQuery {
pub user_id: usize, pub user_id: usize,
#[serde(default)] #[serde(default)]
pub tag: String, pub tag: String,
#[serde(default)]
pub paginated: bool,
} }
/// `/_swiss_army_timeline` /// `/_swiss_army_timeline`
@ -697,6 +699,7 @@ pub async fn swiss_army_timeline_request(
context.insert("list", &list); context.insert("list", &list);
context.insert("page", &req.page); context.insert("page", &req.page);
context.insert("paginated", &req.paginated);
Ok(Html( Ok(Html(
data.1 data.1
.render("timelines/swiss_army.html", &context) .render("timelines/swiss_army.html", &context)

View file

@ -231,6 +231,9 @@ pub struct UserSettings {
/// A list of strings the user has muted. /// A list of strings the user has muted.
#[serde(default)] #[serde(default)]
pub muted: Vec<String>, pub muted: Vec<String>,
/// If timelines are paged instead of infinitely scrolled.
#[serde(default)]
pub paged_timelines: bool,
} }
fn mime_avif() -> String { fn mime_avif() -> String {

View file

@ -41,6 +41,7 @@ pub enum Error {
AlreadyAuthenticated, AlreadyAuthenticated,
DataTooLong(String), DataTooLong(String),
DataTooShort(String), DataTooShort(String),
FileTooLarge,
UsernameInUse, UsernameInUse,
TitleInUse, TitleInUse,
QuestionsDisabled, QuestionsDisabled,
@ -62,6 +63,7 @@ impl Display for Error {
Self::AlreadyAuthenticated => "Already authenticated".to_string(), Self::AlreadyAuthenticated => "Already authenticated".to_string(),
Self::DataTooLong(name) => format!("Given {name} is too long!"), Self::DataTooLong(name) => format!("Given {name} is too long!"),
Self::DataTooShort(name) => format!("Given {name} is too short!"), Self::DataTooShort(name) => format!("Given {name} is too short!"),
Self::FileTooLarge => "Given file is too large".to_string(),
Self::UsernameInUse => "Username in use".to_string(), Self::UsernameInUse => "Username in use".to_string(),
Self::TitleInUse => "Title in use".to_string(), Self::TitleInUse => "Title in use".to_string(),
Self::QuestionsDisabled => "You are not allowed to ask questions there".to_string(), Self::QuestionsDisabled => "You are not allowed to ask questions there".to_string(),