Skip to content

Browse stories by classifier (tag/author/title/url/text)#2102

Open
samuelclay wants to merge 8 commits into
mainfrom
classifier-view
Open

Browse stories by classifier (tag/author/title/url/text)#2102
samuelclay wants to merge 8 commits into
mainfrom
classifier-view

Conversation

@samuelclay

Copy link
Copy Markdown
Owner

Summary

  • New classifier filter banner: click a tag/author/url pill on a story (or the "view matching stories" icon in the trainer) to browse every unread story in the current feed/folder/river that matches that single classifier value. Inline training controls on the banner pill write through save_classifier and recompute story scores on the fly.
  • Filter state lives in the URL as ?classifier_tag=foo / ?classifier_author=foo / ?classifier_title=foo / ?classifier_url=foo / ?classifier_text=foo (plus optional classifier_scope / classifier_folder), so the view is bookmarkable and survives back/forward. Router auto-restores the filter on reload; popstate syncs when only the query string changes.
  • Backend filtering is intentionally "dumb": stories flow through the normal feed/usersub/river loading paths and get narrowed in Python via filter_stories_by_classifier, with a 20x batch multiplier so a single page scan catches every match the unread counts promise. No new indexes required.

Test plan

  • Click a tag pill on a story → "View matching stories" tooltip → banner appears with the clicked value, story list narrows to matching stories
  • Click a second classifier (different type) — URL updates from classifier_tag to classifier_author, no stale keys left behind
  • Reload the page on a filtered URL — banner + filtered story list restore correctly
  • Browser back/forward toggles the banner on and off without a full reload
  • Train the pill to like/dislike/super-dislike — matching stories' sentiment indicators flip immediately, sidebar unread counts refresh, rows stay visible even when hidden
  • Navigate to another feed via the sidebar — banner disappears and the next classifier click on a different feed shows a fresh banner
  • Scope toggles (feed/folder/global) re-issue the filter for Premium Archive users; non-archive users see the "denied" shake
  • "Browse stories by classifier" works for river (all site stories), folder, and single feed views

Generated with Claude Code

samuelclay and others added 8 commits April 10, 2026 19:00
…rl/text

Lets users click a pill in a story or the trainer and jump to a filtered
view of every unread story in the current feed/folder/river that matches
that single classifier value. Filter state is reflected in the URL as
?classifier_tag=... / ?classifier_author=... etc., so it's bookmarkable
and survives back/forward.

Training from the inline banner saves through the same save_classifier
endpoint the trainer uses, recomputes story scores on the fly via
recalculate_story_scores + render:intelligence, and fires feed_unread_count
so sidebar badges update in place. Stories trained to dislike/super-dislike
stay visible in the filter view (threshold forced to -2) so the user can
see exactly what they just trained without rows vanishing out from under
them.

Backend filtering is intentionally "dumb": stories load via the normal
feed/usersub/river paths and are narrowed in Python by
filter_stories_by_classifier. No dedicated ES index — a 20x batch
multiplier on the effective limit gives post-filtering enough raw stories
to catch every match the unread counts promise.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* main:
  Require Android version bump commits to be tagged
  Fix Android auto mark-read so stories flip while still visible
  Bump Android version to 14.2.2 (269)
  Apply black and isort formatting across apps and newsblur_mcp
  Shrink Reading fragment args to avoid TransactionTooLargeException
  Bounds-check WidgetRemoteViewsFactory.getViewAt/getItemId
  Fix ReadingStoryMenuPopup coerceIn crash on narrow windows
  Guard Hilt injection in SyncService and SubscriptionSyncService
  Fix APIResponse crash when widget HTTP call is interrupted
  Fix FolderListFragment ClassCastException on long-press
  Fix NotSerializableException writing Story to activity state Bundle
* main:
  Bump iOS version to 14.5 (build 326)
  Stabilize river story hash test fixtures
  Bump Android version to 14.3
  Add Android story clustering badges and controls
  Add iOS story clustering controls
  Enable IP rate limiting at 500 req/5min to block abusive API clients
  Add Title vs Title+Related cluster mode with per-sibling tier badges
Dark mode selectors used .NB-theme-dark/.NB-theme-black which don't
exist — the actual body class is .NB-dark, so no dark styles applied.

Tags with special characters (e.g. "ai & ml") were double HTML-encoded
because .html() was used instead of .text() to extract tag pill values,
causing the filter to find zero matching stories.

Also simplifies comments and removes redundant code across the
classifier filter view, banner, and backend filter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The scope toggles on the classifier filter banner only shook silently
when non-archive users clicked folder/global scope. The trainer and
notification bell both show a "Requires Premium Archive" tooltip and
flash the toggle amber. Match that behavior on the banner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* main:
  Cap iOS UI smoke test runtime
  Fix CI timeouts and iOS custom-domain test
The bell on the classifier filter banner only bound click, so hover
didn't open the popover. Once opened, the channel indicators vanished
after ~1s because toggling a channel only updated bell data locally,
never persisted to the server, and the mouseleave handler then called
render() which rebuilt the bell from empty server state.

Add mouseenter/mouseleave handlers with the same timer/hover pattern
the trainer uses, make the popover sticky via a _popover_hovered
flag, and pass an on_change callback to ClassifierNotificationPopover
that saves via set_classifier_notification immediately. Drop the
destructive render() on close since the popover already updates the
bell indicators in place.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* main: (63 commits)
  Avoid unread cache rebuilds on all-story rivers
  Fix daily briefing ignoring super-disliked classifiers
  Fix unread status on all-story river pages
  Add sticky story side actions preference
  Add iOS 14.5.4 release notes
  Track MCP usage metrics
  Bump iOS version to 14.5.4
  Handle partial failures in OPML imports
  Fix iOS activity interactions loading
  Bump Android version to 14.3.3
  Log Android reader restore mismatches
  Fix Android reader rotation after mark-read
  Return MCP upstream HTTP failures as tool errors
  Return MCP archive requirement as tool error
  Accept singular feed ID in MCP stories
  Fix MCP resource serialization
  Fix trial users skipped during paid premium upgrade
  Fix Android scroll position lost when leaving and returning to app
  Fix grace period email suppressed across annual expirations
  Fix iOS story detail classifier cycling
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant