Skip to content

feat(llc, core, persistence): Add support for PredefinedFilters on QueryChannels#2709

Open
VelikovPetar wants to merge 10 commits into
v10.0.0from
feature/FLU-357_predefined_filters_v10
Open

feat(llc, core, persistence): Add support for PredefinedFilters on QueryChannels#2709
VelikovPetar wants to merge 10 commits into
v10.0.0from
feature/FLU-357_predefined_filters_v10

Conversation

@VelikovPetar
Copy link
Copy Markdown
Contributor

@VelikovPetar VelikovPetar commented Jun 2, 2026

Submit a pull request

Linear: FLU-357

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Adds support for server-defined predefined filters on queryChannels across the LLC, persistence, and core layers.

A predefined filter is a named filter/sort preset that lives on the server. The client sends predefined_filter (plus optional filter_values / sort_values to interpolate placeholders) instead of an inline filter, and the server echoes back the materialized filter and sort it applied.

stream_chat (LLC)

  • ChannelApi.queryChannels and StreamChatClient.queryChannels accept predefinedFilter, filterValues, sortValues.
  • New StreamChatClient.queryChannelsWithResult returns a QueryChannelsResult exposing both the live channels and the server-resolved PredefinedFilter (name + materialized filter + sort).
  • New PredefinedFilter model and predefined_filter field on QueryChannelsResponse.
  • SortOption<T>.fromJson added so sort specs can round-trip through persistence.
  • New ChatPersistenceClient hooks: getChannelStatesByPredefinedFilter and updateChannelQueriesByPredefinedFilter (default no-op so existing implementations keep compiling).

stream_chat_persistence

  • New channel_query_metadata table that stores the server-resolved filter + sort keyed by the predefined-filter query hash, so offline reads reconstruct the exact resolved spec the server applied.
  • New FilterConverter and ChannelStateSortOrderConverter Drift converters.
  • StreamChatPersistenceClient implements the new hooks; ChannelQueryDao gains updateChannelQueriesByPredefinedFilter and getChannelsAndSpecByPredefinedFilter.

stream_chat_flutter_core

  • StreamChannelListController accepts predefinedFilter, filterValues, sortValues. When set, they take precedence over filter / channelStateSort.
  • The controller tracks a _resolvedChannelStateSort that is overwritten with the server-resolved sort on every successful query, so event-driven inserts keep matching the server's ordering even when the caller only specifies a predefinedFilter.

Sample app

  • ChannelList now uses the stream_chat_flutter_sample_app predefined filter (interpolated with the current user_id) to demonstrate the flow end-to-end.

How to test

  • Run the sample app and confirm the channel list renders, paginates, and reacts to events as before.
  • Toggle offline (airplane mode) after an online query and reopen — channels for the predefined-filter query should be served from persistence, ordered using the server-resolved sort.

Test coverage

  • predefined_filter_test.dart — model.
  • predefined_filter_defaults_test.dart — fallback-sort selection.
  • client_test.dart — online/offline queryChannels paths and queryChannelsWithResult.
  • channel_api_test.dart — request payload assertions.
  • filter_converter_test.dart / sort_order_converter_test.dart — Drift converters.
  • channel_query_dao_test.dart — DAO read/write of cids + metadata.
  • stream_chat_persistence_client_test.dart — persistence client integration.
  • stream_channel_list_controller_test.dart — controller path including resolved-sort tracking.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8df410e7-28f7-4a5d-99ad-311cf5bd103d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/FLU-357_predefined_filters_v10

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

⚠️ Database Entity Files Modified

The following database entity files have been modified in this PR:

packages/stream_chat_persistence/lib/src/entity/channel_queries_metadata.dart
packages/stream_chat_persistence/lib/src/entity/entity.dart

📝 Remember to:

  1. Update database version in db/drift_chat_database.dart.
  2. Update entity schema tests if necessary.

Note: This comment is automatically generated by the CI workflow.

/// Yields the offline-cached result first (when available), followed by
/// the online result. Concurrent identical online queries are coalesced
/// via [_queryChannelsCache].
Stream<QueryChannelsResult> queryChannelsWithResult({
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about the naming here, I tried to make it as explicit as possible. (it wasn't really possible to use the existing queryChannels without a breaking change, as we must change the return type).

/// Carries the live [Channel] instances matching the query alongside the
/// server-resolved [PredefinedFilter] spec (when one is associated with the
/// query).
class QueryChannelsResult {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced this new model to serve as a container for the result of the StreamChatClient.queryChannelsWithResult.

Future<List<Channel>> queryChannelsOnline({
Filter? filter,
SortOrder<ChannelState>? sort,
String? predefinedFilter,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: At the moment I didn't expose public queryChannelsWithResultOnline/queryChannelsWithResultOffline -> only the new queryChannelsWithResults is public. I didn't want to pollute the public API, with methods which most likely will not be used. Let me know if you think we should expose them as well.

// passed options
if (sort != null) 'sort': sort,
if (filter != null) 'filter_conditions': filter,
if (predefinedFilter != null) 'predefined_filter': predefinedFilter,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add any guards preventing the users to send both predefined_filter and filter/sort. The BE will prioritise the predefined filter if available.
Another option would be to:

  1. Fail early (throw error when building the StreamChannelListController, or when calling client.queryChannels)
  2. Override filter/sort with null when predefinedFilter exists

But overall, I a think that letting the server handle this scenario would be the best, I don't want to introduce front-end guards which might become invalid if the server changes the default behaviour.

Let me know if you think we should introduce some front-end checks anyway.

/// falling back to `last_updated` otherwise — this mirrors the server's
/// default sort for channel queries.
SortOrder<ChannelState> get predefinedFilterFallbackSort {
if (_touchesField('last_message_at')) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This follows the BE default sort, if no sort is defined in the predefined filter. See this discussion.

/// Default implementation returns an empty response; persistence
/// implementations that support predefined-filter caching should override
/// this.
Future<QueryChannelsResponse> getChannelStatesByPredefinedFilter({
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it is OK to return QueryChannelsResponse here - or should we introduce a new specific model for this?

@VelikovPetar VelikovPetar marked this pull request as ready for review June 2, 2026 18:04
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

❌ Patch coverage is 94.83871% with 8 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (v10.0.0@803b70d). Learn more about missing BASE report.

Files with missing lines Patch % Lines
packages/stream_chat/lib/src/client/client.dart 92.30% 3 Missing ⚠️
...tream_chat/lib/src/db/chat_persistence_client.dart 0.00% 3 Missing ⚠️
...r_core/lib/src/stream_channel_list_controller.dart 93.75% 1 Missing ⚠️
...hat_persistence/lib/src/dao/channel_query_dao.dart 97.50% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             v10.0.0    #2709   +/-   ##
==========================================
  Coverage           ?   67.94%           
==========================================
  Files              ?      413           
  Lines              ?    24641           
  Branches           ?        0           
==========================================
  Hits               ?    16742           
  Misses             ?     7899           
  Partials           ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@VelikovPetar VelikovPetar requested a review from a team June 3, 2026 10:56
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