Implement global read/unread filter across all category views #34

Closed
opened 2026-06-30 13:55:59 -07:00 by serrebi · 1 comment
Owner

Currently, BlindRSS offers limited flexibility when filtering articles by read status within categorized views. While global views exist for “All Articles”, “Unread Articles”, and “Read Articles”, category-specific views (user-defined categories, Uncategorized) always display all articles from their contained feeds, regardless of read status. The only per-feed filtering option is a context-menu action “Show Only Unread” on an individual feed.

This design makes it difficult to quickly assess unread or read content within a specific category hierarchy. With many categories and feeds, seeing all unread items “dumped” into a single global list (without category context) is inconvenient and reduces the usefulness of the categorization system.

Current behavior:

  • All Articles: shows all articles from all feeds.
  • Unread Articles: shows all unread articles from all feeds (no category structure).
  • Read Articles: shows all read articles from all feeds (no category structure).
  • User-defined categories & Uncategorized: show all articles from their respective feeds (ignoring read/unread status).
  • Per-feed context menu: supports “Show Only Unread” for a single feed.

Expected behavior:

Provide a global filter control (All / Unread / Read) that applies to all category views, preserving the category tree structure. This allows users to see, for example, “only unread articles” organized by their existing categories, or “only read articles” per category.

Proposed Design Changes

1. Simplify Top-Level Category List

Reduce the top-level navigation items to:

  • All Articles – all articles across all feeds.
  • Favorites – all starred/favorite articles.
  • Uncategorized – articles from feeds not assigned to any user category.
  • User-defined named categories – custom categories created by the user.

Remove dedicated top-level entries for “Unread Articles” and “Read Articles” as separate structural nodes; their functionality will be covered by the global filter.

2. Add Global Filter Control

Introduce a prominent filter control (e.g., radio buttons or a dropdown) with options:

  • All
  • Unread
  • Read

This filter should be visible in the main feed/article view and persist across navigation within the category tree.

3. Filtered Category Tree Behavior
  • The category tree (including nested user categories) should dynamically reflect the selected filter.
  • If a category (or subcategory) contains no items matching the active filter (e.g., no unread articles), that category node should be hidden from the tree.
  • The tree structure and ordering should otherwise remain unchanged.
4. Sync and Update Strategy

To avoid confusing UI behavior when articles are marked read/unread:

  • Rebuild/refresh the filtered category tree when:
    • The user changes the global filter (All/Unread/Read).
    • The feed list is refreshed/updated.
  • Do not automatically collapse/remove a category node immediately upon the last article in that category becoming read/unread while the user is interacting with it. This prevents unexpected loss of focus and confusing “vanishing” categories.

Acceptance Criteria

  • The top-level category list no longer includes dedicated “Unread Articles” and “Read Articles” nodes; instead, filtering is handled via the global filter control.
  • A global filter control (radio buttons or dropdown) with “All”, “Unread”, and “Read” options is present and functional.
  • When “Unread” is selected, the category tree shows only categories/subcategories that contain unread articles; categories without unread items are hidden.
  • When “Read” is selected, the category tree shows only categories/subcategories containing read articles; others are hidden.
  • When “All” is selected, all categories and articles are shown, regardless of read status.
  • Filter state persists when navigating between views or refreshing the page (unless explicitly reset).
  • Category tree updates occur on filter change and on feed refresh, but not continuously on every individual article status change to avoid disruptive UI behavior.
  • Performance remains acceptable even with large numbers of feeds and deep category nesting (e.g., 1000+ feeds, 10+ levels of nesting).
  • Favorites and Uncategorized behave consistently with the selected filter (e.g., “Unread” shows only unread favorites/uncategorized items).

Technical Considerations

  • Ensure efficient querying of articles by read/unread status per category, possibly using indexed fields for read_status and category_id.
  • Handle edge cases:
    • Categories with no matching articles under the current filter should be hidden, not displayed with an empty list.
    • Deeply nested categories should be evaluated recursively to determine if any descendant contains matching articles.
    • Mixed scenarios (some subcategories have unread items, others don’t) should correctly show/hide only the relevant branches.
Currently, BlindRSS offers limited flexibility when filtering articles by read status within categorized views. While global views exist for “All Articles”, “Unread Articles”, and “Read Articles”, category-specific views (user-defined categories, Uncategorized) always display *all* articles from their contained feeds, regardless of read status. The only per-feed filtering option is a context-menu action “Show Only Unread” on an individual feed. This design makes it difficult to quickly assess unread or read content within a specific category hierarchy. With many categories and feeds, seeing all unread items “dumped” into a single global list (without category context) is inconvenient and reduces the usefulness of the categorization system. #### Current behavior: - `All Articles`: shows all articles from all feeds. - `Unread Articles`: shows all unread articles from all feeds (no category structure). - `Read Articles`: shows all read articles from all feeds (no category structure). - User-defined categories & `Uncategorized`: show all articles from their respective feeds (ignoring read/unread status). - Per-feed context menu: supports “Show Only Unread” for a single feed. #### Expected behavior: Provide a global filter control (All / Unread / Read) that applies to *all* category views, preserving the category tree structure. This allows users to see, for example, “only unread articles” organized by their existing categories, or “only read articles” per category. #### Proposed Design Changes ##### 1. Simplify Top-Level Category List Reduce the top-level navigation items to: - **All Articles** – all articles across all feeds. - **Favorites** – all starred/favorite articles. - **Uncategorized** – articles from feeds not assigned to any user category. - **User-defined named categories** – custom categories created by the user. Remove dedicated top-level entries for “Unread Articles” and “Read Articles” as separate structural nodes; their functionality will be covered by the global filter. ##### 2. Add Global Filter Control Introduce a prominent filter control (e.g., radio buttons or a dropdown) with options: - **All** - **Unread** - **Read** This filter should be visible in the main feed/article view and persist across navigation within the category tree. ##### 3. Filtered Category Tree Behavior - The category tree (including nested user categories) should dynamically reflect the selected filter. - If a category (or subcategory) contains no items matching the active filter (e.g., no unread articles), that category node should be hidden from the tree. - The tree structure and ordering should otherwise remain unchanged. ##### 4. Sync and Update Strategy To avoid confusing UI behavior when articles are marked read/unread: - Rebuild/refresh the filtered category tree when: - The user changes the global filter (All/Unread/Read). - The feed list is refreshed/updated. - Do **not** automatically collapse/remove a category node immediately upon the last article in that category becoming read/unread while the user is interacting with it. This prevents unexpected loss of focus and confusing “vanishing” categories. --- #### Acceptance Criteria - [ ] The top-level category list no longer includes dedicated “Unread Articles” and “Read Articles” nodes; instead, filtering is handled via the global filter control. - [ ] A global filter control (radio buttons or dropdown) with “All”, “Unread”, and “Read” options is present and functional. - [ ] When “Unread” is selected, the category tree shows only categories/subcategories that contain unread articles; categories without unread items are hidden. - [ ] When “Read” is selected, the category tree shows only categories/subcategories containing read articles; others are hidden. - [ ] When “All” is selected, all categories and articles are shown, regardless of read status. - [ ] Filter state persists when navigating between views or refreshing the page (unless explicitly reset). - [ ] Category tree updates occur on filter change and on feed refresh, but not continuously on every individual article status change to avoid disruptive UI behavior. - [ ] Performance remains acceptable even with large numbers of feeds and deep category nesting (e.g., 1000+ feeds, 10+ levels of nesting). - [ ] Favorites and Uncategorized behave consistently with the selected filter (e.g., “Unread” shows only unread favorites/uncategorized items). --- #### Technical Considerations - Ensure efficient querying of articles by read/unread status per category, possibly using indexed fields for `read_status` and `category_id`. - Handle edge cases: - Categories with no matching articles under the current filter should be hidden, not displayed with an empty list. - Deeply nested categories should be evaluated recursively to determine if any descendant contains matching articles. - Mixed scenarios (some subcategories have unread items, others don’t) should correctly show/hide only the relevant branches. <!-- forgejo-github-sync: issue github=serrebidev/BlindRSS#36 -->
Author
Owner

Implemented in v1.78.0, following your proposed design:

  • Global filter control: View → Article Filter is a radio group with All Articles / Unread Only / Read Only. It applies to every view — categories (with full nesting), individual channels, Favorites, and Uncategorized — and persists across navigation and restarts (restored together with the remembered last view).
  • Simplified top-level list: the dedicated "Unread Articles" and "Read Articles" nodes are removed; their function is covered by the filter while preserving your category structure.
  • Filtered category tree: with Unread or Read active, channels with no matching articles are hidden, and categories are evaluated recursively — a parent stays visible if any descendant has matching items, and branches with none disappear entirely.
  • Sync strategy: exactly as you specified, the tree re-evaluates only on filter change or feed refresh — never immediately when you read the last unread article in a category — so nodes don't vanish under your focus mid-session.
  • Performance: category visibility is computed with memoized recursion (each category evaluated once regardless of depth), and Read mode uses a single grouped SQL query.

One deliberate deviation: the control lives in the View menu as radio items rather than as an on-screen widget — consistent with where "Show Only Unread" landed for #40, and keyboard/screen-reader friendly. Happy to revisit if you'd prefer a visible toolbar control.

Thanks for the thorough design writeup!

Implemented in [v1.78.0](https://github.com/serrebidev/BlindRSS/releases/tag/v1.78.0), following your proposed design: - **Global filter control**: `View → Article Filter` is a radio group with **All Articles / Unread Only / Read Only**. It applies to every view — categories (with full nesting), individual channels, Favorites, and Uncategorized — and persists across navigation and restarts (restored together with the remembered last view). - **Simplified top-level list**: the dedicated "Unread Articles" and "Read Articles" nodes are removed; their function is covered by the filter while preserving your category structure. - **Filtered category tree**: with Unread or Read active, channels with no matching articles are hidden, and categories are evaluated recursively — a parent stays visible if any descendant has matching items, and branches with none disappear entirely. - **Sync strategy**: exactly as you specified, the tree re-evaluates only on filter change or feed refresh — never immediately when you read the last unread article in a category — so nodes don't vanish under your focus mid-session. - **Performance**: category visibility is computed with memoized recursion (each category evaluated once regardless of depth), and Read mode uses a single grouped SQL query. One deliberate deviation: the control lives in the View menu as radio items rather than as an on-screen widget — consistent with where "Show Only Unread" landed for #40, and keyboard/screen-reader friendly. Happy to revisit if you'd prefer a visible toolbar control. Thanks for the thorough design writeup! <!-- forgejo-github-sync: comment github=serrebidev/BlindRSS#36/4877904655 -->
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
serrebi/BlindRSS#34
No description provided.