Skip to content
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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions tests/ui/test_ui_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ def test_mouse_event(self, mocker, msg_view, mouse_scroll_event, widget_size):
@pytest.mark.parametrize("key", keys_for_command("GO_DOWN"))
def test_keypress_GO_DOWN(self, mocker, msg_view, key, widget_size):
size = widget_size(msg_view)
msg_view.model.controller.is_in_empty_narrow = False
msg_view.new_loading = False
mocker.patch(MESSAGEVIEW + ".focus_position", return_value=0)
mocker.patch(MESSAGEVIEW + ".set_focus_valign")
Expand All @@ -291,6 +292,7 @@ def test_keypress_GO_DOWN_exception(
self, mocker, msg_view, key, widget_size, view_is_focused
):
size = widget_size(msg_view)
msg_view.model.controller.is_in_empty_narrow = False
msg_view.new_loading = False
mocker.patch(MESSAGEVIEW + ".focus_position", return_value=0)
mocker.patch(MESSAGEVIEW + ".set_focus_valign")
Expand All @@ -315,6 +317,7 @@ def test_keypress_GO_DOWN_exception(
@pytest.mark.parametrize("key", keys_for_command("GO_UP"))
def test_keypress_GO_UP(self, mocker, msg_view, key, widget_size):
size = widget_size(msg_view)
msg_view.model.controller.is_in_empty_narrow = False
mocker.patch(MESSAGEVIEW + ".focus_position", return_value=0)
mocker.patch(MESSAGEVIEW + ".set_focus_valign")
msg_view.old_loading = False
Expand All @@ -330,6 +333,7 @@ def test_keypress_GO_UP_exception(
self, mocker, msg_view, key, widget_size, view_is_focused
):
size = widget_size(msg_view)
msg_view.model.controller.is_in_empty_narrow = False
msg_view.old_loading = False
mocker.patch(MESSAGEVIEW + ".focus_position", return_value=0)
mocker.patch(MESSAGEVIEW + ".set_focus_valign")
Expand Down
7 changes: 7 additions & 0 deletions tests/ui_tools/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,7 @@ def test_main_view_generates_stream_header(
"color": "#bd6",
},
}
self.model.controller.is_in_empty_narrow = False
last_message = dict(message, **to_vary_in_last_message)
msg_box = MessageBox(message, self.model, last_message)
view_components = msg_box.main_view()
Expand Down Expand Up @@ -890,6 +891,7 @@ def test_main_view_generates_stream_header(
def test_main_view_generates_PM_header(
self, mocker, message, to_vary_in_last_message
):
self.model.controller.is_in_empty_narrow = False
last_message = dict(message, **to_vary_in_last_message)
msg_box = MessageBox(message, self.model, last_message)
view_components = msg_box.main_view()
Expand Down Expand Up @@ -1032,9 +1034,11 @@ def test_msg_generates_search_and_header_bar(
},
}
self.model.narrow = msg_narrow
self.model.controller.is_in_empty_narrow = False
messages = messages_successful_response["messages"]
current_message = messages[msg_type]
msg_box = MessageBox(current_message, self.model, messages[0])

search_bar = msg_box.top_search_bar()
header_bar = msg_box.recipient_header()

Expand Down Expand Up @@ -1405,6 +1409,7 @@ def test_keypress_EDIT_MESSAGE(
report_error = msg_box.model.controller.report_error
report_warning = msg_box.model.controller.report_warning
mocker.patch(MODULE + ".time", return_value=100)
msg_box.model.controller.is_in_empty_narrow = False

msg_box.keypress(size, key)

Expand Down Expand Up @@ -1820,6 +1825,7 @@ def test_reactions_view(
varied_message = dict(message_fixture, **to_vary_in_each_message)
msg_box = MessageBox(varied_message, self.model, None)
reactions = to_vary_in_each_message["reactions"]
msg_box.model.controller.is_in_empty_narrow = False

reactions_view = msg_box.reactions_view(reactions)

Expand Down Expand Up @@ -1976,6 +1982,7 @@ def test_mouse_event_left_click(
mocker.patch.object(msg_box, "keypress")
msg_box.model = mocker.Mock()
msg_box.model.controller.is_in_editor_mode.return_value = compose_box_is_open
msg_box.model.controller.is_in_empty_narrow = False

msg_box.mouse_event(size, "mouse press", 1, col, row, focus)

Expand Down
23 changes: 12 additions & 11 deletions zulipterminal/ui_tools/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,8 @@ def mouse_event(
return super().mouse_event(size, event, button, col, row, focus)

def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if is_command_key("REPLY_MESSAGE", key):
is_in_empty_narrow = self.model.controller.is_in_empty_narrow
Comment on lines -907 to +924
Copy link
Collaborator

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.

if is_command_key("REPLY_MESSAGE", key) and not is_in_empty_narrow:
if self.message["type"] == "private":
self.model.controller.view.write_box.private_box_view(
recipient_user_ids=self.recipient_ids,
Expand All @@ -923,7 +924,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
)
else:
self.model.controller.view.write_box.stream_box_view(0)
elif is_command_key("STREAM_NARROW", key):
elif is_command_key("STREAM_NARROW", key) and not is_in_empty_narrow:
if self.message["type"] == "private":
self.model.controller.narrow_to_user(
recipient_emails=self.recipient_emails,
Expand All @@ -934,7 +935,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
stream_name=self.stream_name,
contextual_message_id=self.message["id"],
)
elif is_command_key("TOGGLE_NARROW", key):
elif is_command_key("TOGGLE_NARROW", key) and not is_in_empty_narrow:
self.model.unset_search_narrow()
if self.message["type"] == "private":
if len(self.model.narrow) == 1 and self.model.narrow[0][0] == "pm-with":
Expand All @@ -958,7 +959,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
topic_name=self.topic_name,
contextual_message_id=self.message["id"],
)
elif is_command_key("TOPIC_NARROW", key):
elif is_command_key("TOPIC_NARROW", key) and not is_in_empty_narrow:
if self.message["type"] == "private":
self.model.controller.narrow_to_user(
recipient_emails=self.recipient_emails,
Expand All @@ -974,20 +975,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.model.controller.narrow_to_all_messages(
contextual_message_id=self.message["id"]
)
elif is_command_key("REPLY_AUTHOR", key):
elif is_command_key("REPLY_AUTHOR", key) and not is_in_empty_narrow:
# All subscribers from recipient_ids are not needed here.
self.model.controller.view.write_box.private_box_view(
recipient_user_ids=[self.message["sender_id"]],
)
elif is_command_key("MENTION_REPLY", key):
elif is_command_key("MENTION_REPLY", key) and not is_in_empty_narrow:
self.keypress(size, primary_key_for_command("REPLY_MESSAGE"))
mention = f"@**{self.message['sender_full_name']}** "
self.model.controller.view.write_box.msg_write_box.set_edit_text(mention)
self.model.controller.view.write_box.msg_write_box.set_edit_pos(
len(mention)
)
self.model.controller.view.middle_column.set_focus("footer")
elif is_command_key("QUOTE_REPLY", key):
elif is_command_key("QUOTE_REPLY", key) and not is_in_empty_narrow:
self.keypress(size, primary_key_for_command("REPLY_MESSAGE"))

# To correctly quote a message that contains quote/code-blocks,
Expand Down Expand Up @@ -1015,7 +1016,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.model.controller.view.write_box.msg_write_box.set_edit_text(quote)
self.model.controller.view.write_box.msg_write_box.set_edit_pos(len(quote))
self.model.controller.view.middle_column.set_focus("footer")
elif is_command_key("EDIT_MESSAGE", key):
elif is_command_key("EDIT_MESSAGE", key) and not is_in_empty_narrow:
# User can't edit messages of others that already have a subject
# For private messages, subject = "" (empty string)
# This also handles the realm_message_content_edit_limit_seconds == 0 case
Expand Down Expand Up @@ -1119,12 +1120,12 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
write_box.header_write_box.focus_col = write_box.FOCUS_HEADER_BOX_TOPIC

self.model.controller.view.middle_column.set_focus("footer")
elif is_command_key("MSG_INFO", key):
elif is_command_key("MSG_INFO", key) and not is_in_empty_narrow:
self.model.controller.show_msg_info(
self.message, self.topic_links, self.message_links, self.time_mentions
)
elif is_command_key("ADD_REACTION", key):
elif is_command_key("ADD_REACTION", key) and not is_in_empty_narrow:
self.model.controller.show_emoji_picker(self.message)
elif is_command_key("MSG_SENDER_INFO", key):
elif is_command_key("MSG_SENDER_INFO", key) and not is_in_empty_narrow:
self.model.controller.show_msg_sender_info(self.message["sender_id"])
return key
2 changes: 2 additions & 0 deletions zulipterminal/ui_tools/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ def mouse_event(
return super().mouse_event(size, event, button, col, row, focus)

def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
if self.model.controller.is_in_empty_narrow:
return super().keypress(size, key)
if is_command_key("GO_DOWN", key) and not self.new_loading:
try:
position = self.log.next_position(self.focus_position)
Expand Down