The original iPodderX layout: toolbar, places, item table, Files pane
- A toolbar across the window with the original's groups: add and unsubscribe, play, mark read and keep for the selected item, scan, a search box for what is showing, and Settings and Log (admins only). - Directory, Popular and All Subscriptions sit at the top of the feed list and open in the main pane; the Popular and Directory buttons and their dialogs are gone. - All Subscriptions lists every item from every feed you subscribe to: GET /api/entries, the per-feed query with its scope widened. The enclosure lookup after it matches files to rows by feed and guid, since a page can now span feeds. - Items are a table (unread, kept, item, feed, file, published) with a Files pane beside it, the text below, and a status bar with totals. On a phone the files follow the text and the table is title and date. - Tests: enclosures are checked in #files; the toolbar's read, keep and play act on the selected item; All Subscriptions holds only your feeds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016HdTEWQNrzyFULijigkmMn
This commit is contained in:
28
src/web.rs
28
src/web.rs
@@ -65,6 +65,7 @@ pub fn router(state: WebState) -> Router {
|
||||
.route("/api/feeds", get(feeds).post(add_feed))
|
||||
.route("/api/feeds/{id}", patch(patch_feed).delete(remove_feed))
|
||||
.route("/api/feeds/{id}/entries", get(entries))
|
||||
.route("/api/entries", get(all_entries))
|
||||
.route("/api/entries/{feed_id}/{guid}/flags", post(set_flags))
|
||||
.route("/api/entries/{feed_id}/{guid}/position", post(set_position))
|
||||
.route("/api/feeds/{id}/read-all", post(read_all))
|
||||
@@ -824,20 +825,37 @@ async fn entries(
|
||||
Path(id): Path<String>,
|
||||
user: crate::db::User,
|
||||
Query(page): Query<Page>,
|
||||
) -> Result<Json<EntryPage>, ApiError> {
|
||||
entry_page(&state, user.id, Some(&id), &page)
|
||||
}
|
||||
|
||||
/// Every subscribed feed's items together, newest first: All Subscriptions.
|
||||
async fn all_entries(
|
||||
State(state): State<WebState>,
|
||||
user: crate::db::User,
|
||||
Query(page): Query<Page>,
|
||||
) -> Result<Json<EntryPage>, ApiError> {
|
||||
entry_page(&state, user.id, None, &page)
|
||||
}
|
||||
|
||||
/// One feed's page of items, or every subscribed feed's when `feed` is None.
|
||||
fn entry_page(
|
||||
state: &WebState,
|
||||
user_id: i64,
|
||||
feed: Option<&str>,
|
||||
page: &Page,
|
||||
) -> Result<Json<EntryPage>, ApiError> {
|
||||
let filter = crate::db::Filter::parse(page.filter.as_deref().unwrap_or("all"));
|
||||
let search = page.q.as_deref().map(str::trim).filter(|q| !q.is_empty());
|
||||
let mut rows = state
|
||||
.ctx
|
||||
.db
|
||||
.entries(user.id, &id, filter, search, page.offset, page.limit.clamp(1, 200))?;
|
||||
let db = &state.ctx.db;
|
||||
let mut rows = db.entries_in(user_id, feed, filter, search, page.offset, page.limit.clamp(1, 200))?;
|
||||
// Feed HTML is untrusted: it reaches the page only after ammonia has been through it.
|
||||
for row in &mut rows {
|
||||
if let Some(d) = &row.description {
|
||||
row.description = Some(ammonia::clean(d));
|
||||
}
|
||||
}
|
||||
let total = state.ctx.db.count_entries(user.id, &id, filter, search)?;
|
||||
let total = db.count_in(user_id, feed, filter, search)?;
|
||||
Ok(Json(EntryPage { total, entries: rows }))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user