diff --git a/crates/app/src/public/html/profile/posts.lisp b/crates/app/src/public/html/profile/posts.lisp
index 0c9d79a..06aca2f 100644
--- a/crates/app/src/public/html/profile/posts.lisp
+++ b/crates/app/src/public/html/profile/posts.lisp
@@ -44,9 +44,10 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
+(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(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 %}")
diff --git a/crates/app/src/public/html/profile/settings.lisp b/crates/app/src/public/html/profile/settings.lisp
index 88c6d59..268dfef 100644
--- a/crates/app/src/public/html/profile/settings.lisp
+++ b/crates/app/src/public/html/profile/settings.lisp
@@ -1403,6 +1403,11 @@
\"Hides dislikes on all posts. Users will also no longer be able to dislike your posts.\",
\"text\",
],
+ [
+ [\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
+ \"{{ profile.settings.paged_timelines }}\",
+ \"checkbox\",
+ ],
[[], \"Fun\", \"title\"],
[
[\"disable_gpa_fun\", \"Disable GPA\"],
diff --git a/crates/app/src/public/html/stacks/feed.lisp b/crates/app/src/public/html/stacks/feed.lisp
index 5698065..5002856 100644
--- a/crates/app/src/public/html/stacks/feed.lisp
+++ b/crates/app/src/public/html/stacks/feed.lisp
@@ -83,9 +83,10 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker")))
+ (text "{% set paged = user and user.settings.paged_timelines %}")
(script
(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 %}"))))
diff --git a/crates/app/src/public/html/timelines/all.lisp b/crates/app/src/public/html/timelines/all.lisp
index c38dd88..7cced78 100644
--- a/crates/app/src/public/html/timelines/all.lisp
+++ b/crates/app/src/public/html/timelines/all.lisp
@@ -33,9 +33,10 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
+(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(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 %}")
diff --git a/crates/app/src/public/html/timelines/following.lisp b/crates/app/src/public/html/timelines/following.lisp
index b1759e4..ef23a55 100644
--- a/crates/app/src/public/html/timelines/following.lisp
+++ b/crates/app/src/public/html/timelines/following.lisp
@@ -11,9 +11,10 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
+(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(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 %}")
diff --git a/crates/app/src/public/html/timelines/home.lisp b/crates/app/src/public/html/timelines/home.lisp
index e398615..5a5658b 100644
--- a/crates/app/src/public/html/timelines/home.lisp
+++ b/crates/app/src/public/html/timelines/home.lisp
@@ -31,9 +31,10 @@
(div ("ui_ident" "io_data_marker")))
(text "{%- endif %}"))
+(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(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 %}")
diff --git a/crates/app/src/public/html/timelines/popular.lisp b/crates/app/src/public/html/timelines/popular.lisp
index 6d26f3d..d0223df 100644
--- a/crates/app/src/public/html/timelines/popular.lisp
+++ b/crates/app/src/public/html/timelines/popular.lisp
@@ -11,9 +11,10 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
+(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(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 %}")
diff --git a/crates/app/src/public/html/timelines/swiss_army.lisp b/crates/app/src/public/html/timelines/swiss_army.lisp
index eb722c9..23243ce 100644
--- a/crates/app/src/public/html/timelines/swiss_army.lisp
+++ b/crates/app/src/public/html/timelines/swiss_army.lisp
@@ -30,3 +30,7 @@
(str (text "chats:label.go_back")))
(text "{%- endif %}"))
(text "{%- endif %}")
+
+(text "{% if paginated -%}")
+(text "{{ components::pagination(page=page, items=list|length) }}")
+(text "{%- endif %}")
diff --git a/crates/app/src/public/js/atto.js b/crates/app/src/public/js/atto.js
index 6c30428..835f76e 100644
--- a/crates/app/src/public/js/atto.js
+++ b/crates/app/src/public/js/atto.js
@@ -1141,7 +1141,7 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
},
);
- self.define("io_data_load", (_, tmpl, page) => {
+ self.define("io_data_load", (_, tmpl, page, paginated_mode = false) => {
self.IO_DATA_MARKER = document.querySelector(
"[ui_ident=io_data_marker]",
);
@@ -1164,7 +1164,16 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
self.IO_DATA_PAGE = page;
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 () => {
diff --git a/crates/app/src/routes/api/v1/auth/images.rs b/crates/app/src/routes/api/v1/auth/images.rs
index e062be1..e177db7 100644
--- a/crates/app/src/routes/api/v1/auth/images.rs
+++ b/crates/app/src/routes/api/v1/auth/images.rs
@@ -213,7 +213,7 @@ pub async fn upload_avatar_request(
if mime == "image/gif" {
// gif image, don't encode
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();
@@ -226,7 +226,7 @@ pub async fn upload_avatar_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
- return Json(Error::DataTooLong("image".to_string()).into());
+ return Json(Error::FileTooLarge.into());
}
// upload image
@@ -314,7 +314,7 @@ pub async fn upload_banner_request(
if mime == "image/gif" {
// gif image, don't encode
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();
@@ -327,7 +327,7 @@ pub async fn upload_banner_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
- return Json(Error::DataTooLong("image".to_string()).into());
+ return Json(Error::FileTooLarge.into());
}
// upload image
diff --git a/crates/app/src/routes/api/v1/communities/images.rs b/crates/app/src/routes/api/v1/communities/images.rs
index 464dede..3ddee00 100644
--- a/crates/app/src/routes/api/v1/communities/images.rs
+++ b/crates/app/src/routes/api/v1/communities/images.rs
@@ -136,7 +136,7 @@ pub async fn upload_avatar_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
- return Json(Error::DataTooLong("image".to_string()).into());
+ return Json(Error::FileTooLarge.into());
}
// upload image
@@ -191,7 +191,7 @@ pub async fn upload_banner_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
- return Json(Error::DataTooLong("image".to_string()).into());
+ return Json(Error::FileTooLarge.into());
}
// upload image
diff --git a/crates/app/src/routes/api/v1/communities/posts.rs b/crates/app/src/routes/api/v1/communities/posts.rs
index 81a1fae..7bf4bf3 100644
--- a/crates/app/src/routes/api/v1/communities/posts.rs
+++ b/crates/app/src/routes/api/v1/communities/posts.rs
@@ -133,7 +133,7 @@ pub async fn create_request(
// check sizes
for img in &images {
if img.len() > MAXIMUM_FILE_SIZE {
- return Json(Error::DataTooLong("image".to_string()).into());
+ return Json(Error::FileTooLarge.into());
}
}
diff --git a/crates/app/src/routes/pages/misc.rs b/crates/app/src/routes/pages/misc.rs
index 3ff3f0d..8b76292 100644
--- a/crates/app/src/routes/pages/misc.rs
+++ b/crates/app/src/routes/pages/misc.rs
@@ -576,6 +576,8 @@ pub struct TimelineQuery {
pub user_id: usize,
#[serde(default)]
pub tag: String,
+ #[serde(default)]
+ pub paginated: bool,
}
/// `/_swiss_army_timeline`
@@ -697,6 +699,7 @@ pub async fn swiss_army_timeline_request(
context.insert("list", &list);
context.insert("page", &req.page);
+ context.insert("paginated", &req.paginated);
Ok(Html(
data.1
.render("timelines/swiss_army.html", &context)
diff --git a/crates/core/src/model/auth.rs b/crates/core/src/model/auth.rs
index a5714c3..bc8b13f 100644
--- a/crates/core/src/model/auth.rs
+++ b/crates/core/src/model/auth.rs
@@ -231,6 +231,9 @@ pub struct UserSettings {
/// A list of strings the user has muted.
#[serde(default)]
pub muted: Vec,
+ /// If timelines are paged instead of infinitely scrolled.
+ #[serde(default)]
+ pub paged_timelines: bool,
}
fn mime_avif() -> String {
diff --git a/crates/core/src/model/mod.rs b/crates/core/src/model/mod.rs
index c50ea7c..62f26a3 100644
--- a/crates/core/src/model/mod.rs
+++ b/crates/core/src/model/mod.rs
@@ -41,6 +41,7 @@ pub enum Error {
AlreadyAuthenticated,
DataTooLong(String),
DataTooShort(String),
+ FileTooLarge,
UsernameInUse,
TitleInUse,
QuestionsDisabled,
@@ -62,6 +63,7 @@ impl Display for Error {
Self::AlreadyAuthenticated => "Already authenticated".to_string(),
Self::DataTooLong(name) => format!("Given {name} is too long!"),
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::TitleInUse => "Title in use".to_string(),
Self::QuestionsDisabled => "You are not allowed to ask questions there".to_string(),