-
Notifications
You must be signed in to change notification settings - Fork 309
msglist: Support @-mentions narrow #807
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
Conversation
lib/model/autocomplete.dart
Outdated
assert(narrow is! CombinedFeedNarrow); | ||
assert(narrow is SendableNarrow); |
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.
This is too strong — this class should work on a stream narrow, too. The point is it should work on any narrow where we offer a compose box.
(Do we have an existing test that fails with this change? If not, a good small side fix would be to add one.)
For purposes of this PR, we can just add one more case to this assert, and probably a one-line comment explaining why it's here. There might be a refactor to do if we start prolifierating to three or four such cases.
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.
We do have test cases that fail this, which pass StreamNarrow
s.
9c5dd67
to
68af8a9
Compare
Reading this chat thread gives me some background on why we need the Looks like we don't support rendering topic mentions yet, but I think we can skip that for now as @-mentions narrow doesn't rely on this feature. |
Regarding whether we should support both |
Yeah, it's fine to leave that flag out if it isn't needed for this feature. If it's easy to include it, though, then it's probably less total work if we include it now than if we add the other flag now and that flag separately later. |
Yeah agreed, let's not try to include support for a future |
f1df782
to
d51d274
Compare
When working on this, I found exhaustiveness checks useful to covering places that need to be updated when adding a narrow. For addings tests, having each file corresponds to a test file helped, but I also needed to do a sweep by searching for Bookkeeping comments like |
39cf1e0
to
dcd451b
Compare
I read the comment on the weird corner-case when messages might be absent in This PR should be ready for an initial review now. |
Yeah, I think it's OK to have to search for existing similar cases (like CombinedFeedNarrow) when adding a new type like this. That TODO line wasn't meant for the purpose of finding all the different places that different types of narrows will need to be handled. It'd be pretty tough to have such comments comprehensively enough to rely much on them — as you saw, there are a lot of places that need updating. If you run the command It would be good to make it so that those exhaustiveness checks were a more comprehensive guide. I think that mostly means factoring out all the conditions that look like this: Probably the most natural home for that is as a static on ComposeBox. That'll mean the one spot in |
Rebased this atop #808, and extracted the |
lib/widgets/compose_box.dart
Outdated
static bool hasComposeBox(Narrow narrow) { | ||
switch (narrow) { | ||
case CombinedFeedNarrow(): | ||
return 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.
This is inverted, right?
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.
Right. Pushed a fix.
179237e
to
5cd83c2
Compare
Rebased to resolve the merge conflicts from the |
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.
Thanks! Small comments below.
Then one question I had was if we want to update parseInternalLink
so that it can return a MentionsNarrow
(I guess just when the link is /#narrow/is/mentioned
). I don't think it's common to want to post a link to the mentions narrow, but it crossed my mind as something that would now be possible to support.
@@ -541,7 +541,7 @@ class RecipientHeader extends StatelessWidget { | |||
final message = this.message; | |||
return switch (message) { | |||
StreamMessage() => StreamMessageRecipientHeader(message: message, | |||
showStream: narrow is CombinedFeedNarrow), | |||
showStream: !ComposeBox.hasComposeBox(narrow)), | |||
DmMessage() => DmRecipientHeader(message: 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.
This doesn't seem like the right reasoning. We include the stream name in recipient headers just when the message list might include messages in different streams, so the user can know which stream each stream message belongs to. The presence/absence of the compose box isn't really part of that story.
I see that we'd like there to be an exhaustive switch, though. We could do that inline, or perhaps out in a helper function. (Not on the compose box, though, since it's not really relevant, as mentioned.)
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.
Yeah. I prefer adding a helper with switch cases in this case.
@@ -129,7 +129,7 @@ class _MessageListPageState extends State<MessageListPage> implements MessageLis | |||
// if those details get complicated, refactor to avoid copying. | |||
// TODO(#311) If we have a bottom nav, it will pad the bottom | |||
// inset, and this should always be true. | |||
removeBottom: widget.narrow is! CombinedFeedNarrow, | |||
removeBottom: ComposeBox.hasComposeBox(widget.narrow), |
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.
Happily I think we can now remove the TODO above this:
// TODO this copies the details of when the compose box is shown;
// if those details get complicated, refactor to avoid copying.
4047c85
to
b89070c
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.
Thanks @PIG208 for building this, and thanks @chrisbobbe for the previous review! Generally this looks great. Comments below.
Also, would you add screenshots of the UI changes? The button on the home page, and the new message-list page.
assets/l10n/app_en.arb
Outdated
@@ -344,6 +344,10 @@ | |||
"@loginErrorMissingUsername": { | |||
"description": "Error message when an empty username was provided." | |||
}, | |||
"mentionsPageTitle": "Mentions", |
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: put next to combinedFeedPageTitle, the most related existing string
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.
Ordering them to be consistent with MessageListAppBarTitle
.
lib/model/narrow.dart
Outdated
} | ||
|
||
@override | ||
int get hashCode => 'MentionedNarrow'.hashCode; |
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: match name of class
@@ -134,5 +134,12 @@ Future<void> _legacyMarkNarrowAsRead(BuildContext context, Narrow narrow) async | |||
messages: unreadDms, | |||
op: UpdateMessageFlagsOp.add, | |||
flag: MessageFlag.read); | |||
case MentionsNarrow(): | |||
final unreadMentions = store.unreads.mentions.toList(); |
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.
Why the copy (the toList
call)?
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.
updateMessageFlags
(which calls connection.post
) expects a List of message ids.
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.
Ah I see, and Unreads.mentions
is a Set. Sounds good, then.
lib/widgets/compose_box.dart
Outdated
static bool hasComposeBox(Narrow narrow) { | ||
switch (narrow) { | ||
case CombinedFeedNarrow(): | ||
case MentionsNarrow(): | ||
return false; | ||
|
||
case StreamNarrow(): |
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: order this switch the same way as the one in build
just below — this is effectively a summary of that one and needs to be kept in sync, so keeping the same ordering helps with verifying that at a glance
lib/widgets/message_list.dart
Outdated
case MentionsNarrow(): | ||
return Text(zulipLocalizations.mentionsPageTitle); |
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: put this case next to CombinedFeedNarrow, as that's the most similar
test/model/message_list_test.dart
Outdated
eg.streamMessage(id: startingId, | ||
stream: stream1, topic: "A", flags: [MessageFlag.wildcardMentioned]), | ||
eg.streamMessage(id: startingId + 1, | ||
stream: stream1, topic: "B", flags: [MessageFlag.wildcardMentioned]), | ||
eg.streamMessage(id: startingId + 2, | ||
stream: stream2, topic: "C", flags: [MessageFlag.wildcardMentioned]), |
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.
When writing a test, a question to ask is: what story does this test need to tell? What is it checking, and what scenarios should it be exercising?
Here, the story that's needed is: messages get included in the view, even messages you might think wouldn't be. Specifically that means: including even a message in a muted topic and muted channel, including even DMs, and including even messages that have flag wildcardMentioned
but not mentioned
.
(The other half of the story would be: messages get excluded from the view, even messages you might think would be included. But for this narrow there are no messages that get excluded, so there's nothing to say in that direction.)
So then the test data should be written to focus on that story. In particular, the messages here that are to an unmuted channel or topic aren't needed — if messages are included even when muted, surely they are when unmuted too.
(It looks like this set of data is based on the one for CombinedFeedNarrow. But there the answer is different for muted vs. unmuted, so it's important to cover both sides.)
test/model/narrow_test.dart
Outdated
check(narrow.containsMessage(eg.streamMessage( | ||
flags: []))).isFalse(); | ||
check(narrow.containsMessage(eg.streamMessage( | ||
flags:[MessageFlag.mentioned]))).isTrue(); | ||
check(narrow.containsMessage(eg.streamMessage( | ||
flags: [MessageFlag.wildcardMentioned]))).isTrue(); |
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: this is a bit hard to read because the isFalse
and isTrue
, which are the most critical bits of information, get visually buried.
This reformatting helps make them stick out:
check(narrow.containsMessage(eg.streamMessage( | |
flags: []))).isFalse(); | |
check(narrow.containsMessage(eg.streamMessage( | |
flags:[MessageFlag.mentioned]))).isTrue(); | |
check(narrow.containsMessage(eg.streamMessage( | |
flags: [MessageFlag.wildcardMentioned]))).isTrue(); | |
check(narrow.containsMessage( | |
eg.streamMessage(flags: []))).isFalse(); | |
check(narrow.containsMessage( | |
eg.streamMessage(flags: [MessageFlag.mentioned]))).isTrue(); | |
check(narrow.containsMessage( | |
eg.streamMessage(flags: [MessageFlag.wildcardMentioned]))).isTrue(); |
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.
Stepping back, though, the main purpose of a test is to keep the code from getting accidentally broken by future changes. Here, I think the biggest risk for accidental breakage is when we add another mentions-related flag, that we might forget to handle it in MentionsNarrow.containsMessage. This test as written won't help in that case — it won't get broken by that.
Probably the most effective thing to do is to make the method's implementation use an exhaustive switch rather than a pair of equality checks. Then it's fine to still have this test, but there's no longer so much relying on it.
test/widgets/actions_test.dart
Outdated
'op': 'add', | ||
'flag': 'read', | ||
}); | ||
await tester.pumpAndSettle(); |
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: overindented; but also do we need this line? the similar test just above doesn't seem to have it
test/model/internal_link_test.dart
Outdated
('/#narrow/stream/7-test-here/is/mentioned', null), | ||
('/#narrow/stream/check/topic/test/is/mentioned', null), |
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.
One more test case to add:
('/#narrow/stream/7-test-here/is/mentioned', null), | |
('/#narrow/stream/check/topic/test/is/mentioned', null), | |
('/#narrow/stream/7-test-here/is/mentioned', null), | |
('/#narrow/stream/check/topic/test/is/mentioned', null), | |
('/#narrow/topic/test/is/mentioned', null), |
This is again on the principle of exercising the cases that are closest to the line between one answer and another, most at risk of going the wrong way. If for example the topicElement != null
check were omitted in the lines this commit adds to the implementation, then I don't think any other test cases would fail, but this one would.
lib/model/internal_link.dart
Outdated
@@ -188,7 +195,10 @@ Narrow? _interpretNarrowSegments(List<String> segments, PerAccountStore store) { | |||
} | |||
} | |||
|
|||
if (dmElement != null) { | |||
if (isMentionedElement != null) { | |||
if (streamElement != null || topicElement != null || dmElement != null) return null; |
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:
if (streamElement != null || topicElement != null || dmElement != null) return null; | |
if (streamElement != null || topicElement != null || dmElement != null) return null; |
Will get back to this next week and complete the update. |
01a03df
to
9caf247
Compare
Updated the PR to
|
7a753cb
to
c769b23
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.
Thanks for the revision! Just one comment below — otherwise all looks good.
lib/model/narrow.dart
Outdated
switch (flag) { | ||
MessageFlag.mentioned => true, | ||
MessageFlag.wildcardMentioned => true, | ||
_ => false |
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.
So in #807 (comment) the point was to make sure that when we add a new mentions-related flag, we don't forget to handle it here. This version doesn't really help with that. 🙂
Instead, list all the flags. That incurs a small cost when we add a non-mentions-related flag, but it's worth it for preventing the class of bug where we forget this for a mentions-related flag.
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.
Good thought, thanks!
@@ -134,5 +134,12 @@ Future<void> _legacyMarkNarrowAsRead(BuildContext context, Narrow narrow) async | |||
messages: unreadDms, | |||
op: UpdateMessageFlagsOp.add, | |||
flag: MessageFlag.read); | |||
case MentionsNarrow(): | |||
final unreadMentions = store.unreads.mentions.toList(); |
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.
Ah I see, and Unreads.mentions
is a Set. Sounds good, then.
d6a64c7
to
2e61521
Compare
Pushed to address the switch case change. Should be ready once it passes the tests. |
Using an exhaustive switch here so we can be reminded of handling new narrows. Signed-off-by: Zixuan James Li <[email protected]>
This will later help ensure the exhaustiveness of similar checks performed on a Narrow. The change is motivated by the need to support new narrows. Signed-off-by: Zixuan James Li <[email protected]>
Maybe we can try to generalize these "ApiNarrowIs"s classes when we support more "is:" operands. Signed-off-by: Zixuan James Li <[email protected]>
Because of how narrow interacts with the entire app, there are test updates all over the place. To check for the completeness of this change, looking at places where CombinedFeedNarrow is referenced can help, because MentionsNarrow is similar to it in many ways. Signed-off-by: Zixuan James Li <[email protected]>
This makes more visually clear the division between the list of flags that count, and the list of flags that don't.
Signed-off-by: Zixuan James Li <[email protected]>
As of now, we don't have generic for the "is" operator, but it will be a easy refactor once we do. Fixes zulip#250. Signed-off-by: Zixuan James Li <[email protected]>
Thanks! Looks good — merging, with a couple of tweaks. See the added commit; and I fixed a couple of summary prefixes from "internal" and "internal_links" to "internal_link". ("Internal" sounds like some much more general meaning — like anything internal to the app.) |
Early prototype to support @-mentions narrow.
TODO:
Evaluate ways to supportDeferred to Support @topic wildcard mentions #817wildcardMetnioned
,channelWildcardMentioned
andtopicWildcardMentioned
with backwards/forward compatibility.Fixes #250