The Directory was one grid of every listed feed, 1,472 in production, under Podcasts and Blogs tabs, a wall of about 30 category buttons and a sort menu, with a picked category's subcategories appearing as a second row of buttons that looked like the first. It read as a list to scroll, and the two rows were hard to tell apart. It opens now on the ten most subscribed feeds, ranked, and the categories as tiles, each with three of its shows' covers fanned in its corner on the tint its initials would get, so the shows colour it and no theme's palette changes. A category has a page of its own: its most subscribed, its subcategories with how many each holds, and its grid. See all is every feed, as the grid was. Podcasts or Blogs holds across every page. Popular, the ten most subscribed, had a place of its own in the feed list; it is the Directory's first section instead, and its key, g p, is gone. /api/popular stays for scripts. The search box finds a feed in the Directory by name (#126). It said "Search items…" there and did nothing, since loadEntries returns early for a place that lists feeds. A feed you do not subscribe to opens a page of its own (#128): its cover, category (a link to that category's page), how many here subscribe, a subscribe button and its latest twenty items, each with its title, linking to the post where it has one, its first lines, date and length. Before, a click on it did nothing; only its + did. The items come from GET /api/directory/{id}, which answers only for a feed the Directory lists, so a guessed id reaches nothing private, and carries no file or its address. The feed's own description would belong at the top, but the database does not keep one. Every tile and chart row takes the keyboard, as the feed list's rows do. Feed text made plain keeps a space at a line break or a paragraph's end, which glued "2010)Recorded" together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
40
src/web.rs
40
src/web.rs
@@ -58,6 +58,7 @@ pub fn router(state: WebState) -> Router {
|
||||
.route("/api/status", get(status))
|
||||
.route("/api/popular", get(get_popular))
|
||||
.route("/api/directory", get(get_directory))
|
||||
.route("/api/directory/{id}", get(get_listed))
|
||||
.route("/api/popular/{id}", post(subscribe_popular))
|
||||
.route("/api/users", get(list_users).post(add_user))
|
||||
.route("/api/users/{id}", patch(patch_user).delete(remove_user))
|
||||
@@ -1010,6 +1011,45 @@ async fn get_directory(
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
/// One of a listed feed's latest items, for its page in the Directory before anyone subscribes
|
||||
/// (#128): what it is, never its files, whose addresses are where a key would be.
|
||||
#[derive(Serialize)]
|
||||
struct ListedItem {
|
||||
guid: String,
|
||||
title: Option<String>,
|
||||
link: Option<String>,
|
||||
published: Option<i64>,
|
||||
duration: Option<i64>,
|
||||
description: Option<String>,
|
||||
}
|
||||
|
||||
/// A listed feed's latest twenty items. Only a feed the Directory lists, so a guessed id reaches
|
||||
/// nothing private, as `subscribe_popular` checks.
|
||||
async fn get_listed(
|
||||
State(state): State<WebState>,
|
||||
user: crate::db::User,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<Vec<ListedItem>>, ApiError> {
|
||||
if !popular(&state, user.id, true).await?.iter().any(|p| p.id == id) {
|
||||
return Err(ApiError::not_found(format!("{id:?} is not in the directory")));
|
||||
}
|
||||
let order = crate::db::order_sql("published", "desc");
|
||||
let rows = state.ctx.db.entries_in(user.id, Some(&id), crate::db::Filter::parse("all"), None, 0, 20, &order).await?;
|
||||
let mut sanitizer = feed_sanitizer();
|
||||
Ok(Json(
|
||||
rows.into_iter()
|
||||
.map(|e| ListedItem {
|
||||
description: e.description.as_deref().map(|d| clean_description(&mut sanitizer, d, e.link.as_deref())),
|
||||
guid: e.guid,
|
||||
title: e.title,
|
||||
link: e.link,
|
||||
published: e.published,
|
||||
duration: e.duration,
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
fn sort_name(p: &PopularRow) -> String {
|
||||
p.title.clone().unwrap_or_else(|| p.id.clone()).to_lowercase()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user