Send a changed feed's row to the page instead of it reloading the list (#105)
After a feed was checked, failed or downloaded a file, and after every item read, the page fetched /api/feeds whole, about 60 ms for 160 rows, though one row had changed. The live event stream now knows who is connected and, after an event that changes a feed, sends that person its row (feed_row), built by the same code as the list (feed_rows, with Db::feed_list asked for one feed). Marking an item read answers with the feed's row. The page puts the row in place and redraws once a frame. A routine skip of a feed not due sends nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
38
src/db.rs
38
src/db.rs
@@ -338,8 +338,14 @@ impl Db {
|
||||
/// What the feed list shows of every feed, for one person, in five queries whatever the
|
||||
/// number of feeds. Asked feed by feed (feed_summary, http_state, blocklist, unread_count)
|
||||
/// it was six round trips a feed, about 950 for 160 feeds and 320 ms a page load (#94).
|
||||
pub async fn feed_list(&self, user_id: i64) -> Result<std::collections::HashMap<String, FeedListing>> {
|
||||
pub async fn feed_list(
|
||||
&self,
|
||||
user_id: i64,
|
||||
// One feed only, for the row a live update sends (`web::feed_rows`).
|
||||
only: Option<&str>,
|
||||
) -> Result<std::collections::HashMap<String, FeedListing>> {
|
||||
use std::collections::HashMap;
|
||||
let one = || sea_orm::Value::from(only.map(str::to_owned));
|
||||
let counts = |sql: &'static str, args: Vec<sea_orm::Value>| async move {
|
||||
self.rows(sql, args)
|
||||
.await?
|
||||
@@ -347,20 +353,29 @@ impl Db {
|
||||
.map(|r| Ok((r.try_get::<String>("", "feed_id")?, r.try_get::<i64>("", "n")?)))
|
||||
.collect::<Result<HashMap<_, _>>>()
|
||||
};
|
||||
let entries = counts("SELECT feed_id, count(*) AS n FROM entries GROUP BY feed_id", vec![]).await?;
|
||||
let downloaded =
|
||||
counts("SELECT feed_id, count(*) AS n FROM enclosures WHERE path IS NOT NULL GROUP BY feed_id", vec![])
|
||||
.await?;
|
||||
let entries = counts(
|
||||
"SELECT feed_id, count(*) AS n FROM entries
|
||||
WHERE (CAST($1 AS TEXT) IS NULL OR feed_id = $1) GROUP BY feed_id",
|
||||
vec![one()],
|
||||
)
|
||||
.await?;
|
||||
let downloaded = counts(
|
||||
"SELECT feed_id, count(*) AS n FROM enclosures
|
||||
WHERE path IS NOT NULL AND (CAST($1 AS TEXT) IS NULL OR feed_id = $1) GROUP BY feed_id",
|
||||
vec![one()],
|
||||
)
|
||||
.await?;
|
||||
let unread = counts(
|
||||
"SELECT e.feed_id, count(*) AS n FROM entries e
|
||||
JOIN subscriptions sub ON sub.user_id = $1 AND sub.feed_id = e.feed_id
|
||||
LEFT JOIN entry_state s
|
||||
ON s.user_id = $1 AND s.feed_id = e.feed_id AND s.guid = e.guid
|
||||
WHERE NOT coalesce(s.read, false)
|
||||
AND (CAST($2 AS TEXT) IS NULL OR e.feed_id = $2)
|
||||
AND NOT EXISTS (SELECT 1 FROM hidden h
|
||||
WHERE h.user_id = $1 AND h.feed_id = e.feed_id AND h.guid = e.guid)
|
||||
GROUP BY e.feed_id",
|
||||
vec![user_id.into()],
|
||||
vec![user_id.into(), one()],
|
||||
)
|
||||
.await?;
|
||||
let mut blocked: HashMap<String, Vec<String>> = blocklists::Entity::find()
|
||||
@@ -370,7 +385,11 @@ impl Db {
|
||||
.into_iter()
|
||||
.map(|b| (b.feed_id, keywords(Some(b.words)).unwrap_or_default()))
|
||||
.collect();
|
||||
Ok(feeds::Entity::find()
|
||||
let mut rows = feeds::Entity::find();
|
||||
if let Some(id) = only {
|
||||
rows = rows.filter(feeds::Column::Id.eq(id));
|
||||
}
|
||||
Ok(rows
|
||||
.all(&self.orm)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -1964,7 +1983,10 @@ mod tests {
|
||||
INSERT INTO blocklists (user_id, feed_id, words) VALUES (1,'g','[\"spoiler\"]');",
|
||||
).await
|
||||
.unwrap();
|
||||
let list = db.feed_list(1).await.unwrap();
|
||||
let list = db.feed_list(1, None).await.unwrap();
|
||||
let g = db.feed_list(1, Some("g")).await.unwrap();
|
||||
assert_eq!(g.keys().collect::<Vec<_>>(), ["g"]);
|
||||
assert_eq!((g["g"].unread, g["g"].summary.entries, g["g"].blocked.clone()), (1, 1, vec!["spoiler".to_string()]));
|
||||
for id in ["f", "g"] {
|
||||
let one = &list[id];
|
||||
let s = db.feed_summary(id).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user