Skip to content

fix(stream): offer every account in the filter, not just the loaded ones - #2886

Open
karlitschek wants to merge 1 commit into
masterfrom
fix/stream-account-filter-any-account
Open

fix(stream): offer every account in the filter, not just the loaded ones#2886
karlitschek wants to merge 1 commit into
masterfrom
fix/stream-account-filter-any-account

Conversation

@karlitschek

Copy link
Copy Markdown
Member

The problem

The stream's "filter by account" dropdown was populated from the activities that happened to be loaded, so it could only offer people whose entries were already on screen — exactly the ones a reader can see without filtering. Anyone further down the stream, or simply quiet lately, could not be picked, and typing their name found nothing because there was no search behind the control.

The fix

Stream accounts come from the stream, not from the page. New endpoint:

GET /ocs/v2.php/apps/activity/api/v2/activity/{filter}/actors
→ [{"id": "alice", "displayName": "Alice Cooper"}, …]

Data::getActors() selects the distinct authors of the viewer's activities through the same applyStreamConditions() the feed and the histogram use, so the list can never disagree with what the stream contains. It deliberately takes no search term, date range or actor — this is the list a restriction is picked from, and one that shrank to whatever is left in the current result could only confirm what is already visible. The filter and object restrictions are honoured, because those select a different stream rather than narrow this one.

Cost: one indexed column read in account-name order, which is the existing activity_filter_by (affecteduser, user, timestamp) index, capped in SQL at 100 rows so the database stops reading as soon as the cap is met. Display names are resolved through IUserManager; deleted and non-local authors keep their account name as the label, since their activities are still in the stream and still filterable.

Everyone else is found by typing. The picker now searches as the reader types, through core's /core/autocomplete/get rather than an account listing of this app's own, so the instance's enumeration rules decide who may be found here exactly as in every other account picker. Stream accounts are listed first — those are the ones with activities behind them — results are deduplicated against them, and a failed search leaves them in place rather than emptying the dropdown, which would read as "nobody by that name". A picked search result keeps its display name after the results are dropped, and the control is no longer hidden when the stream itself reported no accounts.

Notes

  • Picking an account that has nothing in your stream lands on the existing "No matching activities" empty state with its Clear filters button.
  • docs/endpoint-v2.md documents the new endpoint and why it has no search parameter.

Tests

  • Data::getActors(): every author in the stream, deduplicated, other accounts' streams excluded, unaffected by activity age, authorless system events skipped, empty user rejected.
  • APIv2Controller::getActors(): display-name resolution and sorting, fallback for accounts without one, half-given object ignored, unknown filter → 404, anonymous → 403.
  • Frontend: the stream's accounts offered beyond the loaded page, reload on filter change, list request failure tolerated, search results merged and deduplicated, cleared on an empty term, failure keeps the stream accounts, display name retained after picking.

Verified against a real instance: all, by and focused return 200; as a user whose stream holds four accounts, typing a fifth account's name finds them, picking them sets ?actor=… and the stream reacts.

The stream's "filter by account" dropdown was built from the activities that
happened to be loaded, so it could only ever name people whose entries were
already on screen — precisely the ones a reader can see without filtering. An
account further down the stream, or one that had simply been quiet, could not
be picked at all.

The options now come from the stream itself, through a new
`GET /api/v2/activity/{filter}/actors`: the distinct authors of the viewer's
activities, resolved to display names. It deliberately takes no search term,
date range or actor, because this is the list a restriction is picked *from*,
and one that shrank to whatever is left in the current result could only
confirm what is already visible. The filter and object restrictions are
honoured, since those select a different stream rather than narrow this one.
The query rides the existing `activity_filter_by` index, is ordered by account
name and capped in SQL at 100 rows.

That covers everyone the stream knows about. For everyone else the picker now
searches as the reader types, through core's autocomplete endpoint rather than
a listing of this app's own, so the instance's account enumeration rules decide
who may be found here exactly as they do in every other account picker. Stream
accounts are offered first — those are the ones with activities behind them —
and a failed search leaves them in place instead of emptying the dropdown,
which would read as "nobody by that name".

Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.16129% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/views/ActivityAppFeed.vue 88.23% 2 Missing ⚠️
src/components/ActivityFilterBar.vue 96.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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