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,