about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--kittybox-rs/src/database/file/mod.rs12
-rw-r--r--kittybox-rs/src/database/memory.rs13
-rw-r--r--kittybox-rs/src/database/mod.rs10
-rw-r--r--kittybox-rs/src/frontend/mod.rs13
-rw-r--r--kittybox-rs/templates/src/mf2.rs4
-rw-r--r--kittybox-rs/templates/src/templates.rs4
6 files changed, 43 insertions, 13 deletions
diff --git a/kittybox-rs/src/database/file/mod.rs b/kittybox-rs/src/database/file/mod.rs
index 531f7b1..a9de63e 100644
--- a/kittybox-rs/src/database/file/mod.rs
+++ b/kittybox-rs/src/database/file/mod.rs
@@ -499,6 +499,16 @@ impl Storage for FileStorage {
         }
     }
 
+    async fn read_feed_with_cursor(
+        &self,
+        url: &'_ str,
+        cursor: Option<&'_ str>,
+        limit: usize,
+        user: Option<&'_ str>
+    ) -> Result<Option<(serde_json::Value, Option<String>)>> {
+        todo!()
+    }
+
     #[tracing::instrument(skip(self))]
     async fn read_feed_with_limit(
         &self,
@@ -605,7 +615,7 @@ impl Storage for FileStorage {
     }
 
     #[tracing::instrument(skip(self))]
-    async fn set_setting<S: settings::Setting<'a>, 'a>(&self, user: &'_ str, value: S::Data) -> Result<()> {
+    async fn set_setting<S: settings::Setting<'a> + 'a, 'a>(&self, user: &'a str, value: S::Data) -> Result<()> {
         let url = axum::http::Uri::try_from(user).expect("Couldn't parse a URL");
         let mut path = relative_path::RelativePathBuf::new();
         path.push(url.authority().unwrap().to_string());
diff --git a/kittybox-rs/src/database/memory.rs b/kittybox-rs/src/database/memory.rs
index 9d79ecc..36e924f 100644
--- a/kittybox-rs/src/database/memory.rs
+++ b/kittybox-rs/src/database/memory.rs
@@ -238,6 +238,17 @@ impl Storage for MemoryStorage {
         todo!()
     }
 
+    #[allow(unused_variables)]
+    async fn read_feed_with_cursor(
+        &self,
+        url: &'_ str,
+        cursor: Option<&'_ str>,
+        limit: usize,
+        user: Option<&'_ str>
+    ) -> Result<Option<(serde_json::Value, Option<String>)>> {
+        todo!()
+    }
+
     async fn delete_post(&self, url: &'_ str) -> Result<()> {
         self.mapping.write().await.remove(url);
         Ok(())
@@ -249,7 +260,7 @@ impl Storage for MemoryStorage {
     }
 
     #[allow(unused_variables)]
-    async fn set_setting<S: settings::Setting<'a>, 'a>(&self, user: &'_ str, value: S::Data) -> Result<()> {
+    async fn set_setting<S: settings::Setting<'a> + 'a, 'a>(&self, user: &'a str, value: S::Data) -> Result<()> {
         todo!()
     }
 
diff --git a/kittybox-rs/src/database/mod.rs b/kittybox-rs/src/database/mod.rs
index ea4205c..4b1b348 100644
--- a/kittybox-rs/src/database/mod.rs
+++ b/kittybox-rs/src/database/mod.rs
@@ -330,6 +330,14 @@ pub trait Storage: std::fmt::Debug + Clone + Send + Sync {
         user: &'_ Option<String>,
     ) -> Result<Option<serde_json::Value>>;
 
+    async fn read_feed_with_cursor(
+        &self,
+        url: &'_ str,
+        cursor: Option<&'_ str>,
+        limit: usize,
+        user: Option<&'_ str>
+    ) -> Result<Option<(serde_json::Value, Option<String>)>>;
+
     /// Deletes a post from the database irreversibly. 'nuff said. Must be idempotent.
     async fn delete_post(&self, url: &'_ str) -> Result<()>;
 
@@ -337,7 +345,7 @@ pub trait Storage: std::fmt::Debug + Clone + Send + Sync {
     async fn get_setting<S: Setting<'a>, 'a>(&'_ self, user: &'_ str) -> Result<S>;
 
     /// Commits a setting to the setting store.
-    async fn set_setting<S: Setting<'a>, 'a>(&self, user: &'_ str, value: S::Data) -> Result<()>;
+    async fn set_setting<S: Setting<'a> + 'a, 'a>(&self, user: &'a str, value: S::Data) -> Result<()>;
 }
 
 #[cfg(test)]
diff --git a/kittybox-rs/src/frontend/mod.rs b/kittybox-rs/src/frontend/mod.rs
index b90d57c..d677005 100644
--- a/kittybox-rs/src/frontend/mod.rs
+++ b/kittybox-rs/src/frontend/mod.rs
@@ -79,13 +79,13 @@ async fn get_post_from_database<S: Storage>(
     url: &str,
     after: Option<String>,
     user: &Option<String>,
-) -> std::result::Result<serde_json::Value, FrontendError> {
+) -> std::result::Result<(serde_json::Value, Option<String>), FrontendError> {
     match db
-        .read_feed_with_limit(url, &after, POSTS_PER_PAGE, user)
+        .read_feed_with_cursor(url, after.as_deref(), POSTS_PER_PAGE, user.as_deref())
         .await
     {
         Ok(result) => match result {
-            Some(post) => Ok(post),
+            Some((post, cursor)) => Ok((post, cursor)),
             None => Err(FrontendError::with_code(
                 StatusCode::NOT_FOUND,
                 "Post not found in the database",
@@ -125,7 +125,7 @@ pub async fn homepage<D: Storage>(
         get_post_from_database(&db, &path, None, &user),
         get_post_from_database(&db, &feed_path, query.after, &user)
     ) {
-        Ok((hcard, hfeed)) => {
+        Ok(((hcard, _), (hfeed, cursor))) => {
             // Here, we know those operations can't really fail
             // (or it'll be a transient failure that will show up on
             // other requests anyway if it's serious...)
@@ -155,6 +155,7 @@ pub async fn homepage<D: Storage>(
                     content: MainPage {
                         feed: &hfeed,
                         card: &hcard,
+                        cursor: cursor.as_deref(),
                         webring: crate::database::settings::Setting::into_inner(webring)
                     }
                     .to_string(),
@@ -219,7 +220,7 @@ pub async fn catchall<D: Storage>(
         .unwrap();
 
     match get_post_from_database(&db, path.as_str(), query.after, &user).await {
-        Ok(post) => {
+        Ok((post, cursor)) => {
             let (blogname, channels) = tokio::join!(
                 db.get_setting::<crate::database::settings::SiteName>(&host)
                 .map(Result::unwrap_or_default),
@@ -240,7 +241,7 @@ pub async fn catchall<D: Storage>(
                     user,
                     content: match post.pointer("/type/0").and_then(|i| i.as_str()) {
                         Some("h-entry") => Entry { post: &post }.to_string(),
-                        Some("h-feed") => Feed { feed: &post }.to_string(),
+                        Some("h-feed") => Feed { feed: &post, cursor: cursor.as_deref() }.to_string(),
                         Some("h-card") => VCard { card: &post }.to_string(),
                         unknown => {
                             unimplemented!("Template for MF2-JSON type {:?}", unknown)
diff --git a/kittybox-rs/templates/src/mf2.rs b/kittybox-rs/templates/src/mf2.rs
index 0e03dc6..7f943d3 100644
--- a/kittybox-rs/templates/src/mf2.rs
+++ b/kittybox-rs/templates/src/mf2.rs
@@ -344,7 +344,7 @@ markup::define! {
             @PhotoGallery { photos: food["properties"]["photo"].as_array() }
         }
     }
-    Feed<'a>(feed: &'a serde_json::Value) {
+    Feed<'a>(feed: &'a serde_json::Value, cursor: Option<&'a str>) {
         div."h-feed" {
             div.metadata {
                 @if feed["properties"]["name"][0].is_string() {
@@ -359,7 +359,7 @@ markup::define! {
                 @for child in feed["children"].as_array().unwrap() {
                     @match child["type"][0].as_str().unwrap() {
                         "h-entry" => { @Entry { post: child } }
-                        "h-feed" => { @Feed { feed: child } }
+                        "h-feed" => { @Feed { feed: child, cursor: None } }
                         "h-food" => { @Food { food: child } }
                         //"h-event" => { }
                         "h-card" => { @VCard { card: child } }
diff --git a/kittybox-rs/templates/src/templates.rs b/kittybox-rs/templates/src/templates.rs
index 9826bfc..288669d 100644
--- a/kittybox-rs/templates/src/templates.rs
+++ b/kittybox-rs/templates/src/templates.rs
@@ -63,7 +63,7 @@ markup::define! {
             }
         }
     }
-    MainPage<'a>(feed: &'a serde_json::Value, card: &'a serde_json::Value, webring: bool) {
+    MainPage<'a>(feed: &'a serde_json::Value, card: &'a serde_json::Value, cursor: Option<&'a str>, webring: bool) {
         .sidebyside {
             @VCard { card }
             #dynamicstuff {
@@ -90,7 +90,7 @@ markup::define! {
                 }
             }
         }
-        @Feed { feed }
+        @Feed { feed, cursor: *cursor }
     }
     ErrorPage(code: StatusCode, msg: Option<String>) {
         h1 { @format!("HTTP {code}") }