-
Notifications
You must be signed in to change notification settings - Fork 315
Implement basic keyword search #1662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This code had a switch/case on the Narrow type, so I discovered it while implementing keyword-search narrows. We support Zulip Server 7 and later (see README) and refuse to connect to older servers. Since we haven't been using this protocol for servers FL 155+, this should be NFC for all servers we connect to, as long as we know their feature level accurately. Related: zulip#992
Done as preparation to implement a new Narrow subclass for keyword search. We can't really implement a client-side "contains-message" predicate for search; it's up to the server to decide whether a message matches the search query. A simple path to representing that, done here, is to just change Narrow.containsMessage's contract to say that it always returns null if the narrow is a search narrow. The app-code changes show just small behavior changes for a message-list page in a search narrow, which we'll implement soon: - Message events are ignored - Outbox messages never appear Various TODOs in tests; we'll handle all of those as part of adding the keyword-search Narrow subclass.
…ning Seeing that the tests were expecting a notifyListeners call already, we found that one is already done, in MessageStore.handleUpdateMessageEvent, so this is redundant. But because the new call is done synchronously with the existing call, it doesn't trigger any extra rebuilds, and it helps the reader by making it clear locally that listeners do indeed get notified.
We'll use this in keyword search, coming up, for renarrowing when the user submits a new keyword.
Still TODO before shipping: - "There are no messages here" placeholder text -- probably isn't ever right for the search page. Maybe we want one message for when there isn't (yet) a search query, and another message for when we've searched and there are no results. - Tests, ideally - Cut out `match_content` `match_topic` in message objects, for now
ab2bdb6
to
30989af
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exciting! Here's a review of the current version, omitting the points you mentioned as TODOs above.
@@ -281,8 +281,7 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis | |||
}); | |||
} | |||
|
|||
@override | |||
Widget build(BuildContext context) { | |||
AppBar _buildAppBar(BuildContext context) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
msglist [nfc]: Pull out _buildAppBar helper
How about making it a separate widget entirely? Seems like that'd make the separation a bit crisper; and it doesn't look like this method is interacting with this widget's state beyond looking at narrow
.
&& narrow.containsMessage(outboxMessage) | ||
&& narrow.containsMessage(outboxMessage) == true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The app-code changes show just small behavior changes for a
message-list page in a search narrow, which we'll implement soon:
- Message events are ignored
- Outbox messages never appear
nit: s/changes/choices/ — before this commit, there was no answer to the question of how these would behave on a null containsMessage
result, because the types ruled that out
/// Set [narrow] to [newNarrow], reset, [notifyListeners], and [fetchInitial]. | ||
void renarrowAndFetch(Narrow newNarrow) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: mention this from the [narrow] doc, since it's a lot like a setter for that property
class ApiNarrowKeywordSearch extends ApiNarrowElement { | ||
@override String get operator => 'search'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's call this ApiNarrowSearch
, to keep it closely aligned with the underlying API
(unlike the Narrow subclass, which is more about how we organize the UI within the app)
@@ -245,3 +245,16 @@ class ApiNarrowMessageId extends ApiNarrowElement { | |||
negated: json['negated'] as bool? ?? false, | |||
); | |||
} | |||
|
|||
class ApiNarrowKeywordSearch extends ApiNarrowElement { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's put this before with
and id
; those feel more specific than this because they point to an individual message, and naturally go at the end of a list of filters
(I guess that calls for with
to go after is
, too, next to id
.)
/// | ||
/// Will be null for search narrows. | ||
bool? containsMessage(MessageBase message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like this to give a bit more of a semantics for what it means for this to be null:
/// | |
/// Will be null for search narrows. | |
bool? containsMessage(MessageBase message); | |
/// | |
/// Null when the client is unable to predict whether the message | |
/// satisfies the filters of this narrow, e.g. when this is a search narrow. | |
bool? containsMessage(MessageBase message); |
controller: _controller, | ||
autocorrect: false, | ||
|
||
// Current servers seem to require straight quotes for the "exact match"- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
smartQuotesType: SmartQuotesType.disabled, | ||
|
||
autofocus: true, | ||
onSubmitted: (value) => widget.onSubmitted(_valueToNarrow(value)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe have one method do both these steps (since they always occur together):
onSubmitted: (value) => widget.onSubmitted(_valueToNarrow(value)), | |
onSubmitted: _handleSubmitted, |
border: OutlineInputBorder( | ||
borderRadius: BorderRadius.circular(10), | ||
borderSide: BorderSide.none), | ||
hintStyle: TextStyle(color: designVariables.labelSearchPrompt))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should go next to hintText
case MentionsNarrow(): | ||
case StarredMessagesNarrow(): | ||
check(model.narrow.containsMessage(message)).isNotNull().isTrue(); | ||
case KeywordSearchNarrow(): | ||
check(model.narrow.containsMessage(message)).isNull(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a KeywordSearchNarrow, then there's nothing that needs to be checked here; it's a fact about that narrow type, not about the list of messages, that containsMessage
will always be null.
Then I think also this is one place where we don't need to be considering for each new narrow type how it should be handled. We can let the default be that the check is done; if at some point we add another narrow type where the check would fail, then we'll naturally notice at test time, and can fix it then. So this could look like:
if (model.narrow is! KeywordSearchNarrow) {
check(model.narrow.containsMessage(message)).isNotNull().isTrue();
}
Hmm or maybe better yet: this could just check that containsMessage
is either null or true. If it's null, we're unable to confirm whether the message really belongs here, so checkInvariants
has to let it slide. What it's really looking for is cases where it's false, and therefore MessageListView really should have omitted the message.
(All the comments above are small, except #1662 (comment) about … Yeah, confirmed: the app only ever calls that function for generating a message link, so with a SendableNarrow.) |
Stacked on #1658
Still TODO before shipping, but I wanted to push my progress before heading home:
Cut out
match_content
match_topic
in message objects, for now"There are no messages here" placeholder text -- probably isn't
ever right for the search page. Maybe we want one message for when
there isn't (yet) a search query, and another message for when
we've searched and there are no results.
Tests.
Screenshots, for Alya's review.
Fixes #252.