Skip to content

topic_list: Enable topic filtering - #2326

Open
sm-sayedi wants to merge 5 commits into
zulip:mainfrom
sm-sayedi:1384-topic-filtering
Open

topic_list: Enable topic filtering#2326
sm-sayedi wants to merge 5 commits into
zulip:mainfrom
sm-sayedi:1384-topic-filtering

Conversation

@sm-sayedi

@sm-sayedi sm-sayedi commented May 24, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1384.

Figma design: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6821-37083&t=IUNKC8IkYmIvIb5d-0

Screenshots
Before After After (Dark)
Screenshot 2026-05-24 at 5 22 54 PM Screenshot 2026-05-24 at 5 08 46 PM Screenshot 2026-05-24 at 5 18 48 PM
Focused Text entered Text entered (Dark)
Screenshot 2026-05-24 at 5 35 12 PM Screenshot 2026-05-24 at 5 31 16 PM Screenshot 2026-05-24 at 5 31 35 PM
Empty filtered topics Empty filtered topics (Dark)
Screenshot 2026-05-24 at 5 37 04 PM Screenshot 2026-05-24 at 5 37 09 PM
Screen recording
Screen.Recording.2026-05-24.at.5.08.12.PM.mov

@sm-sayedi sm-sayedi added the maintainer review PR ready for review by Zulip maintainers label May 24, 2026
@sm-sayedi
sm-sayedi requested a review from chrisbobbe May 24, 2026 13:10
@sm-sayedi
sm-sayedi requested review from rajveermalviya and removed request for chrisbobbe May 24, 2026 13:11

@rajveermalviya rajveermalviya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @sm-sayedi! Comments below, including one about the query implementation, haven't looked at the tests yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: commit message

icons: Edit `search` icon to remove the additional space around it
The icon was edited in Inkspace and cleaned up using the following guide:
    https://zulip.readthedocs.io/en/latest/subsystems/icons.html#preparing-icons-for-use-with-zulip

s/Inkspace/Inkscape

Comment thread lib/widgets/topic_list.dart Outdated
@@ -161,6 +167,9 @@ class _TopicListState extends State<_TopicList> with PerAccountStoreAwareStateMi

@override
Widget build(BuildContext context) {
final store = PerAccountStoreWidget.of(context);
final zulipLocalization = ZulipLocalizations.of(context);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: zulipLocalizations (not zulipLocalization).
Also remove another instance of final zulipLocalizations = ZulipLocalizations.of(context); below.

Comment thread lib/widgets/topic_list.dart Outdated
@override
Widget build(BuildContext context) {
final designVariables = DesignVariables.of(context);
final zulipLocalization = ZulipLocalizations.of(context);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: zulipLocalizations (not zulipLocalization).

Comment thread lib/widgets/topic_list.dart Outdated
Comment on lines +184 to +188
final filteredTopics = _filterQuery == null
? channelTopics
: channelTopics.where((e) =>
(e.name.displayName ?? store.realmEmptyTopicDisplayName).toLowerCase()
.contains(_filterQuery!.toLowerCase()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we want this search to behave in the same way the current topic autocomplete behaves. See #1384 (comment).

So we should probably use relevant bits from lib/model/autocomplete.dart for topic autocomplete, just like emoji picker search.

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch 2 times, most recently from c385b09 to 3ce261b Compare May 27, 2026 15:53
@sm-sayedi

Copy link
Copy Markdown
Collaborator Author

Thanks @rajveermalviya for the review. Changes pushed, PTAL.

@sm-sayedi
sm-sayedi requested a review from rajveermalviya May 27, 2026 15:53

@rajveermalviya rajveermalviya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the revision @sm-sayedi! All LGTM, some small comments below.

After addressing them please mark it for the integration review.

Comment thread lib/widgets/emoji_reaction.dart Outdated
void _handleViewModelUpdate() {
setState(() {
_resultsToDisplay = List.unmodifiable(_viewModel!.results);
_resultsToDisplay = List.unmodifiable(_viewModel?.results ?? []);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: _handleViewModelUpdate will always have non-null _viewModel, right?

Suggested change
_resultsToDisplay = List.unmodifiable(_viewModel?.results ?? []);
_resultsToDisplay = List.unmodifiable(_viewModel!.results ?? []);

Comment thread lib/widgets/topic_list.dart Outdated

class _TopicListState extends State<_TopicList> with PerAccountStoreAwareStateMixin {
Topics? topicsModel;
final TextEditingController _controller = TextEditingController();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: let's move this instantiation inside initState, and make this a late declaration.

Suggested change
final TextEditingController _controller = TextEditingController();
late TextEditingController _controller;

Comment thread lib/widgets/topic_list.dart Outdated
Comment on lines +161 to +163
_controller.dispose();
_topicAutocompleteModel?.dispose();
unreadsModel?.removeListener(_unreadsModelChanged);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The order of these should be reverse of their initialization.

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from 3ce261b to 85d085a Compare June 10, 2026 20:17
@sm-sayedi

Copy link
Copy Markdown
Collaborator Author

Thanks Rajesh for the review. Changes pushed, marking for Chris's review.

@sm-sayedi sm-sayedi added integration review Added by maintainers when PR may be ready for integration and removed maintainer review PR ready for review by Zulip maintainers labels Jun 10, 2026
@sm-sayedi
sm-sayedi requested a review from chrisbobbe June 10, 2026 20:19
@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from 85d085a to b85fd69 Compare June 10, 2026 20:24
@chrisbobbe

Copy link
Copy Markdown
Collaborator

Looks like this has gathered some conflicts; would you rebase please? I hope to get to this soon.

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from b85fd69 to 82e2889 Compare July 16, 2026 07:06
@sm-sayedi

Copy link
Copy Markdown
Collaborator Author

(Conflicts resolved.)

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from 82e2889 to 3ebabb0 Compare July 18, 2026 04:21

@chrisbobbe chrisbobbe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Comments below.

Comment thread lib/widgets/message_list.dart Outdated
Comment on lines +795 to +796
child: Icon(size: 24, ZulipIcons.search)),
child: SizedBox.square(dimension: 24,
child: Icon(size: 20, ZulipIcons.search))),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

icons: Edit `search` icon to remove the additional space around it

This change looks like it adds, or restores, some amount of additional space around the icon? What's the actual intent of this commit? If this change is part of that intent, why isn't the ZulipIcons.search in lib/widgets/home.dart also touched?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the catch. The search icon in other places should've been changed too; done in the new revision. The only exception is the search icon in the main-menu sheet. The Figma design seems to be inconsistent with the icon sizes of the menu buttons, ranging from 20px to 24px. We use 24px for all of them.

The actual intent of the commit is to have the search icon without any surrounding space, so we can exactly match the Figma design where the icon is used. Discovered the need for this while implementing the search field in the topic list page.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The Figma design seems to be inconsistent with the icon sizes of the menu buttons, ranging from 20px to 24px.

Worth being precise about this, I think: I don't actually see an inconsistency here. I'm seeing consistent 24px squares for all the menu buttons, at the icons/24/foo level:

image

There's a nested node on all of them that's less than 24px, e.g. 22px with the "clock" icon:

image

where the square box wraps the icon's glyph tightly, leaving no surrounding whitespace. But the icon asset we actually export from the Figma—linked by the "go to main component" button—is the 24px version with the surrounding whitespace:

image

That looks to be the case with the "search" icon too:

image

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

So I don't think there's a change to be made here, and we should just keep the asset unchanged, without adding padding in the widget tree to compensate anywhere. Am I missing something, though?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The inconsistency that I was referring to was about the nested node inside the 24px square box. But now that you mentioned that we export the main icon component that has internal padding, we can drop the commit.

}
return topic.displayName != raw
&& AutocompleteQuery.lowercaseAndStripDiacritics(topic.displayName!).contains(_normalized);
return AutocompleteQuery.lowercaseAndStripDiacritics(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

autocomplete: Include the result when the query matches the topic

Commit-message nit: let's mention "topic autocomplete" in the summary line. Maybe:

autocomplete: In topic autocomplete, include exact matches

Comment thread lib/model/autocomplete.dart Outdated
final int channelId;

Iterable<TopicName> _topics = [];
Iterable<GetChannelTopicsEntry> _topics = [];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

autocomplete: Change TopicAutocompleteResult.topic type

Can this be marked NFC, or is there some behavior change I'm missing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yep, that's NFC. Thanks for bringing it up.

Comment thread lib/model/autocomplete.dart Outdated
required this.channelId,
});
}) {
store.topics.addListener(_fetch);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

autocomplete: Search again when there is a change in topics

This code is more indirect than it needs to be: a topics change triggers _fetch, which doesn't actually fetch anything (the data is already present, right), and then that calls _startSearch. This would read more transparently if it were store.topics.addListener(_startSearch).

How about:

  • In a prep commit, remove the mutable _topics field and the .._fetch() in TopicAutocompleteView.init. Instead, put final topics = await store.topics.getChannelTopics(channelId) at the top of computeResults.
  • Then this line in this commit can change to store.topics.addListener(_startSearch).

?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

That's a nice improvement, thanks.

Comment thread lib/widgets/topic_list.dart Outdated
Comment on lines +225 to +227
],
),
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: coalesce closing parens

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch 3 times, most recently from 7602c9e to f467175 Compare July 24, 2026 17:09
@sm-sayedi

Copy link
Copy Markdown
Collaborator Author

Thanks for the review, Chris. Revision pushed.

Also, dropped the commit autocomplete: Make AutocompleteView.results nullable as it wasn't needed anymore after the new commit autocomplete [nfc]: In TopicAutocomplete, fetch topics in computeResults.

@sm-sayedi
sm-sayedi requested a review from chrisbobbe July 24, 2026 17:11

@chrisbobbe chrisbobbe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Small comment below with a finding from reading Git history, and see my reply above: #2326 (comment)

return AutocompleteQuery.lowercaseAndStripDiacritics(store.realmEmptyTopicDisplayName)
.contains(_normalized);
}
return topic.displayName != raw

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

autocomplete: In topic autocomplete, include exact matches

Interesting: in code review, it looks like Greg requested the != raw condition be dropped, for the same reason you give—we want to match web—but it seems that request wasn't acted on, and it slipped through and got merged. So you could additionally link to that review in the commit message, to support the change: #627 (comment)

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from f467175 to d21a92c Compare July 25, 2026 04:22
@sm-sayedi

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. Changes pushed with that icon commit dropped!

@sm-sayedi
sm-sayedi requested a review from chrisbobbe July 25, 2026 04:30
@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from d21a92c to 35a5c57 Compare July 25, 2026 04:40

@chrisbobbe chrisbobbe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! Here's a partial review before I have to run to dinner; more later.

I noticed something this time in manual testing: this has a bug like #1175, which I think should be fixed the same way: when you change your query, it should jump back to the top (the "zero" scroll position) so you can see the most relevant results.

Comment thread lib/model/autocomplete.dart Outdated
Comment on lines +1250 to +1252
Future<List<TopicAutocompleteResult>?> computeResults() async {
// TODO: handle fetch failure
final topics = await store.topics.getChannelTopics(channelId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

autocomplete [nfc]: In TopicAutocomplete, fetch topics in computeResults

This isn't NFC, because now we're letting the user re-trigger the fetch by changing the query, if it failed the first time.

@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch 4 times, most recently from 0c2ae40 to 400fe2e Compare July 28, 2026 11:46
@sm-sayedi

Copy link
Copy Markdown
Collaborator Author

Thanks for those points. New changes pushed. Also addressed some feedback from a review Claude did.

@sm-sayedi
sm-sayedi requested a review from chrisbobbe July 28, 2026 11:48
Previously, in topic autocomplete, the query "t" would not
include a topic named "t" (an exact match). I think this is not desired
and it's not what the web does.

This also matches a suggestion Greg made in an earlier review of
this part of the code:
  zulip#627 (comment)
Change it from TopicName to GetChannelTopicsEntry.
This allows future commits to access maxId when the
TopicAutocomplete machinery is used for topic filtering
on the topic-list page.
Move the topic fetch from TopicAutocompleteView.init to computeResults.
This simplifies the implementation by awaiting the cached
Topics.getChannelTopics call directly, instead of restarting the
search after the fetch completes.

Another benefit is that user can re-trigger the topic-list fetch by
changing the query; if initially failed.

The main motivation for this change is that it will enable us to
cleanly search again when the topics change.
This enables the UI to reflect changes to the topics.
This will be needed in the next commit(s), where we use the
TopicAutocomplete machinery for topic filtering on the
topic-list page.
@sm-sayedi
sm-sayedi force-pushed the 1384-topic-filtering branch from 400fe2e to b7c5085 Compare August 4, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration review Added by maintainers when PR may be ready for integration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Filter topics in topic-list screen

3 participants