Skip to content

Add per-account feed filter preferences#13

Open
blindndangerous wants to merge 9 commits intomasonasons:masterfrom
blindndangerous:feature/feed-filter-preferences
Open

Add per-account feed filter preferences#13
blindndangerous wants to merge 9 commits intomasonasons:masterfrom
blindndangerous:feature/feed-filter-preferences

Conversation

@blindndangerous
Copy link
Contributor

@blindndangerous blindndangerous commented Mar 23, 2026

Summary

  • Adds a Feed Filters tab to the Options dialog (Ctrl+,) so each GitHub account can independently control which events appear in their activity feed
  • Filters are applied at render time — the full event list stays cached, so switching filters is instant without a re-fetch
  • Three filter layers applied in order:
    1. Muted repositories — hide all events from specific owner/repo strings (blacklist)
    2. Per-user overrides — configure per-actor event visibility; empty set = mute that user entirely
    3. Global event type/action whitelist — granular action-level control (e.g. PullRequestEvent:merged separate from PullRequestEvent:closed)
  • Merged PRs use a synthetic merged action distinct from closed
  • PullRequestReviewEvent filters by review state (approved/changes_requested/commented), not the always-submitted payload action
  • CreateEvent/DeleteEvent filter by ref type (branch/tag/repository)
  • Filters are stored per-account in account{N}/config.json; new accounts default to showing everything

Test plan

  • 165 tests in tests/test_feed_filters.py — all pass (pytest tests/test_feed_filters.py)
  • Open Options (Ctrl+,) → confirm two tabs: General and Feed Filters
  • Feed Filters tab shows 5 grouped sections; action-level checkboxes under each event type that has sub-actions
  • Uncheck PullRequestEvent > Merged → Apply → merged PRs disappear from feed; reopen Options → still unchecked
  • Mute a repo → Apply → all events from that repo hidden
  • Add a user filter → only that user's selected event types appear; empty selection mutes them
  • Switch accounts → each account has independent filter settings
  • Config JSON for account contains feed_visible_event_types, feed_muted_repos, feed_user_filters keys

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@users.noreply.github.com>
Human Testing Verification: blindndangerous

blindndangerous and others added 9 commits March 22, 2026 14:01
- Add models/feed_filter.py: pure-Python whitelist filter logic
  (load/save visible types, filter_feed, is_event_visible)
- Add tests/test_feed_filters.py: 51 tests covering all edge cases,
  corrupt data, roundtrips, and account isolation
- GUI/main.py: apply filter in _render_feed_list, cache to
  _visible_feed, fix get_selected_feed_event index mismatch
- GUI/options.py: restructure with wx.Notebook (General + Feed
  Filters tabs), 19 grouped checkboxes, Select/Deselect All,
  Enter activates OK, Apply no longer shows confirmation dialog
- build.py: add models.feed_filter hidden import, --collect-all wx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- models/feed_filter.py: add load/save_muted_repos and MUTED_REPOS_KEY;
  update filter_feed to apply muted repo blacklist before type whitelist
- tests/test_feed_filters.py: 31 new tests covering load/save/corrupt
  data, filter interaction, roundtrips, and account isolation (93 total)
- GUI/main.py: pass muted_repos to filter_feed in _render_feed_list
- GUI/options.py: add Muted Repositories section to Feed Filters tab
  with ListBox, owner/repo text entry, Add/Remove buttons and validation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
EVT_TEXT_ENTER requires the TE_PROCESS_ENTER style flag on the TextCtrl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- models/feed_filter.py: add load/save_user_filters and USER_FILTERS_KEY;
  update filter_feed with user override layer (muted repo > user rule >
  global type filter)
- tests/test_feed_filters.py: 34 new tests covering load/save/corrupt
  data, all three filter layers interacting, roundtrips, and account
  isolation (127 total)
- GUI/main.py: pass user_filters to filter_feed in _render_feed_list
- GUI/options.py: add User Filters section to Feed Filters tab with
  ListBox, Add/Edit/Remove buttons; add UserFilterDialog sub-dialog
  with grouped type checkboxes and mute-entirely shortcut

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Config.__setitem__ wraps any stored dict value in a Config (MutableMapping)
subclass. load_user_filters was checking isinstance(raw, dict) which always
failed for stored user filters, causing it to return None on every load.

Fix: use isinstance(raw, Mapping) from collections.abc to accept both plain
dicts and Config-wrapped mappings. Add regression test using a FakeConfig
that mimics the same MutableMapping-not-dict pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UserFilterDialog: remove read-only on username field so it can be edited
- Normalize usernames and repo names to lowercase on add/save
- Disable Edit and Remove buttons for user filters until an item is selected
- Disable Remove button for muted repos until an item is selected
- Add descriptive label above muted repo entry field to clarify its purpose
- Re-disable Edit/Remove after deleting an entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Usernames and repo names are stored lowercase, but GitHub API returns
actor logins and repo names in their original case. filter_feed was
doing case-sensitive lookups so user filters and muted repos never matched.

Fix: lowercase e.actor.login and e.repo.name before lookup. Add 3
regression tests covering mixed-case actor and repo scenarios.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously all 19 types were checked for new users, so adding a user
without changing anything had no effect on the feed. Changed default to
all-unchecked so adding a user immediately hides all their events unless
specific types are explicitly selected. Added clarifying hint text to
the dialog.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Event types with sub-actions (PullRequestEvent, IssuesEvent, etc.) now
filter at the individual action level using "Type:action" keys instead of
a single checkbox per type. Merged PRs use a synthetic "merged" key
distinct from "closed". PullRequestReviewEvent uses review.state
(approved/changes_requested/commented) rather than the always-"submitted"
payload action. CreateEvent/DeleteEvent use ref_type (branch/tag/repository).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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