Popular is a top 10; a Directory lists every listed feed A to Z
- GET /api/popular returns the ten most subscribed feeds. The new GET
/api/directory returns every feed that may be listed, sorted by name,
from the same list: everyone counted, you included, never a URL, never
a private feed or a feed inside an OPML. POST /api/popular/{id} still
subscribes to anything on it.
- A Directory button sits beside Popular; both open the same list
screen. The sidebar toolbar wraps rather than squeezing four buttons.
- Tests: the directory is A to Z, Popular is its top ten, Paid Show is
in neither, and subscribing works from the directory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016HdTEWQNrzyFULijigkmMn
This commit is contained in:
26
src/web.rs
26
src/web.rs
@@ -75,6 +75,7 @@ pub fn router(state: WebState) -> Router {
|
||||
.route("/api/opml", get(export_opml).post(import_opml))
|
||||
.route("/api/settings", get(get_settings).patch(patch_settings))
|
||||
.route("/api/popular", get(get_popular))
|
||||
.route("/api/directory", get(get_directory))
|
||||
.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))
|
||||
@@ -565,9 +566,9 @@ struct PopularRow {
|
||||
subscribed: bool,
|
||||
}
|
||||
|
||||
/// What everyone here subscribes to, you included, most subscribers first. What the Popular
|
||||
/// button and the Add feed screen show, and all that `subscribe_popular` will subscribe
|
||||
/// you to.
|
||||
/// Every feed that may be listed, with everyone counted, you included, most subscribers
|
||||
/// first. Popular is the top of it, the directory is all of it, and it is all that
|
||||
/// `subscribe_popular` will subscribe you to.
|
||||
fn popular(state: &WebState, user_id: i64) -> Result<Vec<PopularRow>> {
|
||||
let db = &state.ctx.db;
|
||||
let mine: std::collections::HashSet<String> =
|
||||
@@ -586,8 +587,7 @@ fn popular(state: &WebState, user_id: i64) -> Result<Vec<PopularRow>> {
|
||||
let subscribed = mine.contains(&s.id);
|
||||
out.push(PopularRow { id: s.id, title: sum.title, image: sum.image, subscribers: n, subscribed });
|
||||
}
|
||||
let name = |p: &PopularRow| p.title.clone().unwrap_or_else(|| p.id.clone()).to_lowercase();
|
||||
out.sort_by(|a, b| b.subscribers.cmp(&a.subscribers).then_with(|| name(a).cmp(&name(b))));
|
||||
out.sort_by(|a, b| b.subscribers.cmp(&a.subscribers).then_with(|| sort_name(a).cmp(&sort_name(b))));
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
@@ -596,10 +596,24 @@ async fn get_popular(
|
||||
user: crate::db::User,
|
||||
) -> Result<Json<Vec<PopularRow>>, ApiError> {
|
||||
let mut rows = popular(&state, user.id)?;
|
||||
rows.truncate(20);
|
||||
rows.truncate(10);
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
/// Every feed that may be listed, A to Z.
|
||||
async fn get_directory(
|
||||
State(state): State<WebState>,
|
||||
user: crate::db::User,
|
||||
) -> Result<Json<Vec<PopularRow>>, ApiError> {
|
||||
let mut rows = popular(&state, user.id)?;
|
||||
rows.sort_by_key(sort_name);
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
fn sort_name(p: &PopularRow) -> String {
|
||||
p.title.clone().unwrap_or_else(|| p.id.clone()).to_lowercase()
|
||||
}
|
||||
|
||||
/// Subscribes by id, because the list never shows a URL. Checked against the same list, so
|
||||
/// a guessed id cannot reach a private feed.
|
||||
async fn subscribe_popular(
|
||||
|
||||
Reference in New Issue
Block a user