diff --git a/crates/app/src/public/html/timelines/all.lisp b/crates/app/src/public/html/timelines/all.lisp
index 7cced78..d739b2e 100644
--- a/crates/app/src/public/html/timelines/all.lisp
+++ b/crates/app/src/public/html/timelines/all.lisp
@@ -36,7 +36,7 @@
(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, \"{{ paged }}\" === \"true\"]);
+ trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=AllPosts&before=$1$&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 535dbe9..02edda2 100644
--- a/crates/app/src/public/html/timelines/swiss_army.lisp
+++ b/crates/app/src/public/html/timelines/swiss_army.lisp
@@ -9,7 +9,7 @@
(datalist
("ui_ident" "list_posts_{{ page }}")
(text "{% for post in list -%}")
- (option ("value" "{{ post[0].id }}"))
+ (option ("value" "{{ post[0].id }}") ("data-created" "{{ post[0].created }}"))
(text "{%- endfor %}"))
(text "{% if list|length == 0 -%}")
(div
diff --git a/crates/app/src/public/js/atto.js b/crates/app/src/public/js/atto.js
index 6503548..d3b4bbb 100644
--- a/crates/app/src/public/js/atto.js
+++ b/crates/app/src/public/js/atto.js
@@ -1212,6 +1212,7 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
self.IO_HAS_LOADED_AT_LEAST_ONCE = false;
self.IO_DATA_DISCONNECTED = false;
self.IO_DATA_DISABLE_RELOAD = false;
+ self.IO_DATA_LOAD_BEFORE = 0;
if (!paginated_mode) {
self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER);
@@ -1256,7 +1257,9 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
// ...
const text = await (
- await fetch(`${self.IO_DATA_TMPL}${self.IO_DATA_PAGE}`)
+ await fetch(
+ `${self.IO_DATA_TMPL.replace("&before=$1$", `&before=${self.IO_DATA_LOAD_BEFORE}`)}${self.IO_DATA_PAGE}`,
+ )
).text();
self.IO_DATA_WAITING = false;
@@ -1291,34 +1294,6 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
self.IO_DATA_ELEMENT.children.length - 1
].after(self.IO_DATA_MARKER);
- // remove posts we've already seen
- function remove_elements(id, outer = false) {
- let idx = 0;
- for (const element of Array.from(
- document.querySelectorAll(
- `.post${outer ? "_outer" : ""}\\:${id}`,
- ),
- )) {
- if (element.getAttribute("is_repost") === true) {
- continue;
- }
-
- if (idx === 0) {
- idx += 1;
- continue;
- }
-
- // everything that isn't the first element should be removed
- element.remove();
- console.log("removed duplicate post");
- }
- }
-
- for (const id of self.IO_DATA_SEEN_IDS) {
- remove_elements(id, false);
- remove_elements(id, true); // scoop up questions
- }
-
// push ids
for (const opt of Array.from(
document.querySelectorAll(
@@ -1330,6 +1305,8 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""}
if (!self.IO_DATA_SEEN_IDS[v]) {
self.IO_DATA_SEEN_IDS.push(v);
}
+
+ self.IO_DATA_LOAD_BEFORE = opt.getAttribute("data-created");
}
}, 150);
diff --git a/crates/app/src/routes/api/v1/communities/posts.rs b/crates/app/src/routes/api/v1/communities/posts.rs
index e2e608e..7ea70e5 100644
--- a/crates/app/src/routes/api/v1/communities/posts.rs
+++ b/crates/app/src/routes/api/v1/communities/posts.rs
@@ -838,7 +838,7 @@ pub async fn all_request(
};
match data
- .get_latest_posts(12, props.page, &Some(user.clone()))
+ .get_latest_posts(12, props.page, &Some(user.clone()), props.before)
.await
{
Ok(posts) => {
diff --git a/crates/app/src/routes/pages/misc.rs b/crates/app/src/routes/pages/misc.rs
index 1abc14b..141ec25 100644
--- a/crates/app/src/routes/pages/misc.rs
+++ b/crates/app/src/routes/pages/misc.rs
@@ -631,6 +631,8 @@ pub struct TimelineQuery {
pub tag: String,
#[serde(default)]
pub paginated: bool,
+ #[serde(default)]
+ pub before: usize,
}
/// `/_swiss_army_timeline`
@@ -688,7 +690,9 @@ pub async fn swiss_army_timeline_request(
// everything else
match req.tl {
DefaultTimelineChoice::AllPosts => {
- data.0.get_latest_posts(12, req.page, &user).await
+ data.0
+ .get_latest_posts(12, req.page, &user, req.before)
+ .await
}
DefaultTimelineChoice::PopularPosts => {
data.0.get_popular_posts(12, req.page, 604_800_000).await
diff --git a/crates/app/src/routes/pages/mod.rs b/crates/app/src/routes/pages/mod.rs
index 909fa2d..abc0b32 100644
--- a/crates/app/src/routes/pages/mod.rs
+++ b/crates/app/src/routes/pages/mod.rs
@@ -157,6 +157,8 @@ pub async fn render_error(
pub struct PaginatedQuery {
#[serde(default)]
pub page: usize,
+ #[serde(default)]
+ pub before: usize,
}
#[derive(Deserialize)]
diff --git a/crates/core/src/database/auth.rs b/crates/core/src/database/auth.rs
index b6a820f..3cadb2e 100644
--- a/crates/core/src/database/auth.rs
+++ b/crates/core/src/database/auth.rs
@@ -725,14 +725,16 @@ impl DataManager {
self.cache_clear_user(&other_user).await;
// create audit log entry
- self.create_audit_log_entry(AuditLogEntry::new(
- user.id,
- format!(
- "invoked `update_user_purchased_status` with x value `{}` and y value `{}`",
- other_user.id, x
- ),
- ))
- .await?;
+ if user.id != other_user.id {
+ self.create_audit_log_entry(AuditLogEntry::new(
+ user.id,
+ format!(
+ "invoked `update_user_purchased_status` with x value `{}` and y value `{}`",
+ other_user.id, x
+ ),
+ ))
+ .await?;
+ }
// ...
Ok(())
diff --git a/crates/core/src/database/posts.rs b/crates/core/src/database/posts.rs
index 2c5aed8..cc864cd 100644
--- a/crates/core/src/database/posts.rs
+++ b/crates/core/src/database/posts.rs
@@ -1374,6 +1374,7 @@ impl DataManager {
batch: usize,
page: usize,
as_user: &Option,
+ before_time: usize,
) -> Result> {
let hide_answers: bool = if let Some(user) = as_user {
user.settings.all_timeline_hide_answers
@@ -1389,7 +1390,12 @@ impl DataManager {
let res = query_rows!(
&conn,
&format!(
- "SELECT * FROM posts WHERE replying_to = 0 AND NOT context LIKE '%\"is_nsfw\":true%'{} ORDER BY created DESC LIMIT $1 OFFSET $2",
+ "SELECT * FROM posts WHERE replying_to = 0{} AND NOT context LIKE '%\"is_nsfw\":true%'{} ORDER BY created DESC LIMIT $1 OFFSET $2",
+ if before_time > 0 {
+ format!(" AND created < {before_time}")
+ } else {
+ String::new()
+ },
if hide_answers {
" AND context::jsonb->>'answering' = '0'"
} else {
diff --git a/crates/core/src/database/stacks.rs b/crates/core/src/database/stacks.rs
index c4fa5df..6a64b53 100644
--- a/crates/core/src/database/stacks.rs
+++ b/crates/core/src/database/stacks.rs
@@ -56,7 +56,7 @@ impl DataManager {
match stack.sort {
StackSort::Created => {
self.fill_posts_with_community(
- self.get_latest_posts(batch, page, &user).await?,
+ self.get_latest_posts(batch, page, &user, 0).await?,
as_user_id,
&ignore_users,
user,