-
-
Notifications
You must be signed in to change notification settings - Fork 252
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
Handling Empty Narrows #1543
base: main
Are you sure you want to change the base?
Handling Empty Narrows #1543
Conversation
It was previously showing a text "DONT HIDE".
Co-authored-by: Sumanth V Rao <[email protected]>
Updated tests.
Co-authored-by: Sumanth V Rao <[email protected]>
Message type can only be "stream" or "private", as returned by the API. Simplified the flow to bunch if-else blocks together. Test removed.
Tests updated. Co-authored-by: Sumanth V Rao <[email protected]>
08eccc4
to
24f7e2f
Compare
24f7e2f
to
2a66297
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.
@Niloth-p I know I replied in the channel, but wanted to follow up here here too.
Broadly this looks great, though there are queries over which specific parts we cover, eg. which keys dummy messages respond to. There are also possibly-missing aspects to consider sooner or later, such as how and when to narrow from a dummy message.
I think each specific feedback comment should be easy to follow, but let me know if not.
I'm increasingly thinking the empty-narrow might be best tracked in the model along with the 'current narrow' and associated data?
I think I addressed your outstanding aspects, but I'd add it only to the last one, and maybe only when it's close to ready to avoid spamming the issue ;) (see a recent czo topic!)
Oh, one thing I did find that I didn't note elsewhere is that the DM area doesn't indicate which recipients you're chatting with. This is a side-effect of how we handle the two sections at the top, but makes me wonder if the current-message-recipients should show the intended narrow/recipients, even if there are none. In a comment I wondered about treating the recipients as blank, but this would be clearer? Re DMs specifically, if I do enter in the user list to someone new, it usefully makes it empty, but as soon as I exit the user it treats it as some kind of generic narrow - x
doesn't resume that user (which isn't indicated). I'm not sure what happens in other narrows?
if is_command_key("REPLY_MESSAGE", key): | ||
is_in_empty_narrow = self.model.controller.is_in_empty_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.
If using the message as a placeholder, then I understand the need to disable lots of these hotkey behaviors. However, rather than disabling each of them individually, it would be a lot clearer to branch/exit separately, likely first.
Currently you seem to be allowing only the following:
- start a stream message
- narrow to all messages
Should we allow a new DM message (x) too?
Some actions you're excluding clearly don't make a lot of sense, when they have to act on a specific valid message, but I'm not sure about other cases, specifically narrowing.
Should all narrowing be possible from an empty narrow? Maybe a stream/channel narrow is empty, so we can try an all-messages narrow, but then what if that is itself empty? That suggests we can try different (absolute) narrows from any empty narrow?
How does that work 'without' a message id, ie. an empty narrow and 'fake' message? What does the message id fall back to in that case?
Nothing like this works before this PR, so the simplest solution might be to start from including no narrowing operations, and treat this as a followup after discussion.
self.msg_narrow = urwid.Text("DONT HIDE") | ||
self.msg_narrow = urwid.Text("") |
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.
As I said in the channel, it's great to remove this line, though I think this was associated with the comment some lines above, which we could also remove? (look through the git log via git blame?)
In terms of this commit, it would be helpful to clarify under which conditions the application would display an empty recipient bar, since I very rarely actually see this odd text. This doesn't need to be via an exact analysis, but notes would be helpful, since looking back to this change in the future would help clarify what happens here.
self.msg_narrow = urwid.Text("DONT HIDE") | ||
self.msg_narrow = urwid.Text("") |
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 was going to suggest taking advantage of this small commit to eg. refactor the attribute naming to be private. However, this is one of those coupled classes/objects, so many of these names are actually public, so this likely would be best settled with a separate refactoring effort.
In the absence of that, you could add comments to clarify each attribute for now; would that help you next time? If so, it'd help others who next read the code :)
@@ -598,6 +599,7 @@ def _narrow_to(self, anchor: Optional[int], **narrow: Any) -> None: | |||
if len(msg_id_list) == 0 or (anchor is not None and anchor not in msg_id_list): | |||
self.model.get_messages(num_before=30, num_after=10, anchor=anchor) | |||
msg_id_list = self.model.get_message_ids_in_current_narrow() | |||
self.is_in_empty_narrow = bool(len(msg_id_list) == 0) |
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.
Was this small commit factored out, so that it would cover Sumanth's original process here? Oh, I think you said that it was where the inspiration came from?
(I'm torn whether this belongs in the model or controller here, but that's probably a refactoring to consider how to structure the whole narrowing behavior)
if self.message["type"] == "stream": | ||
if self.model.controller.is_in_empty_narrow: | ||
return self.empty_narrow_header() | ||
elif self.message["type"] == "stream": |
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 a partial fix towards the issue? That is, we start to show a different message in the message header and the current-message header? Assuming so, the commit text could indicate that.
Does the new header structure need to follow the form of the others? I know it's rather hacky in places, so it's likely fine for now.
dummy_message = { | ||
"id": None, | ||
"content": ( | ||
"No search hits" if model.is_search_narrow() else "No messages here" |
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 couldn't trigger 'No search hits' from here, and trying to do so without the last commit triggered a crash instead.
The 'No messages here' works fine as a placeholder for now, though I'm not sure of the text.
@@ -198,8 +198,23 @@ def private_header(self) -> Any: | |||
header.markup = title_markup | |||
return header | |||
|
|||
def empty_narrow_header(self) -> Any: | |||
title_markup = ("header", [("general_narrow", "No selected 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'm wondering if it would be clearer to simply leave this blank?
else None, | ||
"subject": next( | ||
(subnarrow[1] for subnarrow in model.narrow if subnarrow[0] == "topic"), | ||
"No topics in channel", |
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 possible, but difficult to achieve with the current model with Zulip - I think stream events would have to be absent. Did you manually test this?
# Add a dummy message if no new or old messages are found | ||
if not message_list and not last_msg: | ||
message_type = "stream" if model.stream_id is not None else "private" | ||
dummy_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.
While I know that the MessageBox
is designed to render a complex data type, I feel we should be able to avoid this kind of hack - this branch already adds some internal knowledge of a 'dummy' type to that class, so a 'make this a dummy' request on initialization might well make this a lot simpler too?
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.
To clarify
- I know that the function in this file is used a lot and it makes it simpler to centralize the calling point here
- passing in a complex 'fake' message that has to have all the values to render properly seems more complicated than saying 'please make this a dummy with these parameters'?
self.controller.exit_editor_mode() | ||
self.controller.search_messages(self.text_box.edit_text) | ||
self.controller.view.middle_column.set_focus("body") | ||
if self.controller.search_messages(self.text_box.edit_text): | ||
self.controller.exit_editor_mode() | ||
self.controller.view.middle_column.set_focus("body") | ||
else: | ||
self.controller.report_error(["No results found."]) |
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'm not sure if we want to take this approach rather than just have the empty result like the rest of this branch is pursuing, but it's an interesting extra change - I assume this is extra to the original version?
What does this PR do, and why?
Fixes all the bugs related to empty narrows, and recipient bars when a message is not selected.
External discussion & connections
topic
Solution Space Exploration
Before settling on the dummy message approach, I explored a few other solutions as well:
I explored not switching narrows to empty narrows. We'd need to do some refactoring to
get_message_ids_in_current_narrow
to allow checking if a future narrow is empty, before switching to it.And I also explored whether we could expand our system of handling narrows to also cater to narrows without messages, without handling it as an exception case and introducing a dummy message. I tried to move
top_search_bar
andrecipient_header
outside ofMessageBox
to change how they're tightly tied to the focused message, which is why they're not updated currently when in an empty narrow, and the erratic behavior.Instead of a dummy message, I gave a shot at using a Text widget replacing the entire MessageView, like we do with the
PanelSearchBox
es when there are no search hits. But yes, I ran into a few cases with focus issues.I tried moving the Text element inside, and modifying/turning off the
ModListWalker
's action each time, to make it behave similar to theSimpleFocusListWalker
, but that still wasn't a clean fix.And other similar tangents, before realising the dummy message approach is the solid one, even if we were to implement any of these in the future, especially the 1st approach, the dummy message should be a backup.
I'm sure a more experienced person would have been able to figure this out much faster than I did, and without all this exploration, so I'd be interested in hearing the thinking process of someone who was able to / can narrow in on (pun intended) the dummy message solution right away.
I also decided against removing the message recipients section for empty narrows, because currently when opening the app, it shows up with "DONT HIDE", so I went with a solution that conforms to that behavior.
Outstanding aspect(s)
Next Steps
I didn't want to hold back on the reviews any longer, so I've pushed the PR and will look into this soon.
Follow-ups
How did you test this?
Self-review checklist for each commit