-
-
Notifications
You must be signed in to change notification settings - Fork 259
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
Make the ALL_MESSAGES command work globally #1512
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
import time | ||
from collections import defaultdict | ||
from contextlib import contextmanager | ||
from enum import Enum | ||
from functools import partial, wraps | ||
from itertools import chain, combinations | ||
from re import ASCII, MULTILINE, findall, match | ||
|
@@ -49,6 +50,12 @@ | |
StreamAccessType = Literal["public", "private", "web-public"] | ||
|
||
|
||
class SearchStatus(Enum): | ||
DEFAULT = 0 | ||
FILTERED = 1 | ||
EMPTY = 2 | ||
|
||
Comment on lines
+53
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a really interesting approach! I think it would be great to include a docstring here as well, explaining this a bit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't add any because none of the other classes in helper.py have docstrings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems fair. I was just thinking it might be confusing for people looking at the code later, so a docstring would help clarify it. However, now that you mention it, it might not be necessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good naming > docstrings/comments, but it could be helpful if the naming can't be easily improved - and it's good not to block on getting perfect names ;) |
||
|
||
class StreamData(TypedDict): | ||
name: str | ||
id: int | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ | |
) | ||
from zulipterminal.config.ui_sizes import LEFT_WIDTH | ||
from zulipterminal.helper import ( | ||
SearchStatus, | ||
TidiedUserInfo, | ||
asynch, | ||
match_emoji, | ||
|
@@ -335,7 +336,7 @@ def __init__(self, streams_btn_list: List[Any], view: Any) -> None: | |
), | ||
) | ||
self.search_lock = threading.Lock() | ||
self.empty_search = False | ||
self.search_status = SearchStatus.DEFAULT | ||
|
||
@asynch | ||
def update_streams(self, search_box: Any, new_text: str) -> None: | ||
|
@@ -352,7 +353,11 @@ def update_streams(self, search_box: Any, new_text: str) -> None: | |
)[0] | ||
|
||
streams_display_num = len(streams_display) | ||
self.empty_search = streams_display_num == 0 | ||
self.search_status = ( | ||
SearchStatus.EMPTY | ||
if streams_display_num == 0 | ||
else SearchStatus.FILTERED | ||
) | ||
|
||
# Add a divider to separate pinned streams from the rest. | ||
pinned_stream_names = [ | ||
|
@@ -371,7 +376,7 @@ def update_streams(self, search_box: Any, new_text: str) -> None: | |
streams_display.insert(first_unpinned_index, StreamsViewDivider()) | ||
|
||
self.log.clear() | ||
if not self.empty_search: | ||
if self.search_status == SearchStatus.FILTERED: | ||
self.log.extend(streams_display) | ||
else: | ||
self.log.extend([self.stream_search_box.search_error]) | ||
|
@@ -398,14 +403,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: | |
self.stream_search_box.set_caption(" ") | ||
self.view.controller.enter_editor_mode_with(self.stream_search_box) | ||
return key | ||
elif is_command_key("CLEAR_SEARCH", key): | ||
elif ( | ||
is_command_key("CLEAR_SEARCH", key) | ||
and self.search_status != SearchStatus.DEFAULT | ||
): | ||
self.stream_search_box.reset_search_text() | ||
self.log.clear() | ||
self.log.extend(self.streams_btn_list) | ||
self.set_focus("body") | ||
self.log.set_focus(self.focus_index_before_search) | ||
self.search_status = SearchStatus.DEFAULT | ||
self.view.controller.update_screen() | ||
return key | ||
elif is_command_key("ALL_MESSAGES", key): | ||
self.view.home_button.activate(key) | ||
return super().keypress(size, key) | ||
|
||
|
||
|
@@ -436,7 +447,7 @@ def __init__( | |
header=self.header_list, | ||
) | ||
self.search_lock = threading.Lock() | ||
self.empty_search = False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's not on this PR to solve this, but it's easy to notice here: It's unfortunate that there's a lot of duplication between all the different views. A similar logic change repeated three times. I'm currently not sure what could be a good way to refactor this. Maybe a class that knows the view state, handles the keypresses, and forwards back to the view what should happen based on some interface. |
||
self.search_status = SearchStatus.DEFAULT | ||
|
||
def _focus_position_for_topic_name(self) -> int: | ||
saved_topic_state = self.view.saved_topic_in_stream_id( | ||
|
@@ -461,10 +472,14 @@ def update_topics(self, search_box: Any, new_text: str) -> None: | |
for topic in self.topics_btn_list.copy() | ||
if new_text in topic.topic_name.lower() | ||
] | ||
self.empty_search = len(topics_to_display) == 0 | ||
self.search_status = ( | ||
SearchStatus.EMPTY | ||
if len(topics_to_display) == 0 | ||
else SearchStatus.FILTERED | ||
) | ||
|
||
self.log.clear() | ||
if not self.empty_search: | ||
if self.search_status == SearchStatus.FILTERED: | ||
self.log.extend(topics_to_display) | ||
else: | ||
self.log.extend([self.topic_search_box.search_error]) | ||
|
@@ -518,14 +533,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: | |
self.topic_search_box.set_caption(" ") | ||
self.view.controller.enter_editor_mode_with(self.topic_search_box) | ||
return key | ||
elif is_command_key("CLEAR_SEARCH", key): | ||
elif ( | ||
is_command_key("CLEAR_SEARCH", key) | ||
and self.search_status != SearchStatus.DEFAULT | ||
): | ||
self.topic_search_box.reset_search_text() | ||
self.log.clear() | ||
self.log.extend(self.topics_btn_list) | ||
self.set_focus("body") | ||
self.log.set_focus(self.focus_index_before_search) | ||
self.search_status = SearchStatus.DEFAULT | ||
self.view.controller.update_screen() | ||
return key | ||
elif is_command_key("ALL_MESSAGES", key): | ||
self.view.home_button.activate(key) | ||
return super().keypress(size, key) | ||
|
||
|
||
|
@@ -665,7 +686,7 @@ def __init__(self, view: Any) -> None: | |
|
||
self.allow_update_user_list = True | ||
self.search_lock = threading.Lock() | ||
self.empty_search = False | ||
self.search_status = SearchStatus.DEFAULT | ||
super().__init__(self.users_view(), header=search_box) | ||
|
||
@asynch | ||
|
@@ -706,10 +727,12 @@ def update_user_list( | |
else: | ||
users_display = users | ||
|
||
self.empty_search = len(users_display) == 0 | ||
self.search_status = ( | ||
SearchStatus.EMPTY if len(users_display) == 0 else SearchStatus.FILTERED | ||
) | ||
|
||
# FIXME Update log directly? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not certain but imo it should be fine. I assume that the comment (added in this commit) means moving some of the logic from RightColumnView to UsersView which holds log. And this should be fine, as we already support TopicsView and StreamsView with the new enum, instead of the LeftColumnView. So, it would probably require just moving the enum to a different class. |
||
if not self.empty_search: | ||
if self.search_status != SearchStatus.EMPTY: | ||
self.body = self.users_view(users_display) | ||
else: | ||
self.body = UsersView( | ||
|
@@ -759,14 +782,20 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: | |
self.user_search.set_caption(" ") | ||
self.view.controller.enter_editor_mode_with(self.user_search) | ||
return key | ||
elif is_command_key("CLEAR_SEARCH", key): | ||
elif ( | ||
is_command_key("CLEAR_SEARCH", key) | ||
and self.search_status != SearchStatus.DEFAULT | ||
): | ||
self.user_search.reset_search_text() | ||
self.allow_update_user_list = True | ||
self.body = UsersView(self.view.controller, self.users_btn_list) | ||
self.set_body(self.body) | ||
self.set_focus("body") | ||
self.search_status = SearchStatus.DEFAULT | ||
self.view.controller.update_screen() | ||
return key | ||
elif is_command_key("ALL_MESSAGES", key): | ||
self.view.home_button.activate(key) | ||
elif is_command_key("GO_LEFT", key): | ||
self.view.show_right_panel(visible=False) | ||
return super().keypress(size, key) | ||
|
@@ -926,6 +955,8 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: | |
return key | ||
elif is_command_key("GO_RIGHT", key): | ||
self.view.show_left_panel(visible=False) | ||
elif is_command_key("ALL_MESSAGES", key) and self.get_focus() is self.menu_v: | ||
self.view.home_button.activate(key) | ||
return super().keypress(size, key) | ||
|
||
|
||
|
@@ -2027,7 +2058,7 @@ def __init__( | |
search_box = urwid.Pile( | ||
[self.emoji_search, urwid.Divider(SECTION_DIVIDER_LINE)] | ||
) | ||
self.empty_search = False | ||
self.search_status = SearchStatus.DEFAULT | ||
self.search_lock = threading.Lock() | ||
super().__init__( | ||
controller, | ||
|
@@ -2073,10 +2104,14 @@ def update_emoji_list( | |
else: | ||
self.emojis_display = self.emoji_buttons | ||
|
||
self.empty_search = len(self.emojis_display) == 0 | ||
self.search_status = ( | ||
SearchStatus.EMPTY | ||
if len(self.emojis_display) == 0 | ||
else SearchStatus.FILTERED | ||
) | ||
|
||
body_content = self.emojis_display | ||
if self.empty_search: | ||
if self.search_status == SearchStatus.EMPTY: | ||
body_content = [self.emoji_search.search_error] | ||
|
||
self.contents["body"] = ( | ||
|
@@ -2150,5 +2185,6 @@ def keypress(self, size: urwid_Size, key: str) -> str: | |
self.emoji_search.reset_search_text() | ||
self.controller.exit_editor_mode() | ||
self.controller.exit_popup() | ||
self.search_status = SearchStatus.DEFAULT | ||
return key | ||
return super().keypress(size, key) |
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 enum is an improvement overall 👍 It acknowledges that there are actually three distinct states for the search status in the code.
Re implementation in this commit structure:
FILTERED
, but fewer changes forEMPTY
, and none forDEFAULT
; doing the split as per the above bullet would make it clearer where new state tracking was added and extra asserts added, vs renaming.For the enum element names:
DEFAULT
is OK, but in terms of a search-status, it's not as clear as it could be if that's search-on or search-off.UNFILTERED
is maybe too close toFILTERED
, but is closer to the meaning of it?EMPTY
is rather moreFILTERED_EMPTY
? We could end up in a state where it's "UNFILTERED_EMPTY" (DEFAULT_EMPTY), but I don't think the code covers that case?Re location, helper.py is OK, though I'd prefer we reduce elements in here since 'helper' is such a generic name :)