diff --git a/tests/ui/test_ui_tools.py b/tests/ui/test_ui_tools.py index 072fa1caa4..d47720d4bb 100644 --- a/tests/ui/test_ui_tools.py +++ b/tests/ui/test_ui_tools.py @@ -15,7 +15,8 @@ from zulipterminal.ui_tools.views import ( AboutView, HelpView, LeftColumnView, MessageView, MiddleColumnView, ModListWalker, MsgInfoView, PopUpConfirmationView, PopUpView, - RightColumnView, StreamInfoView, StreamsView, TopicsView, UsersView, + RightColumnView, StreamInfoView, StreamsView, StreamsViewDivider, + TopicsView, UsersView, ) from zulipterminal.version import MINIMUM_SUPPORTED_SERVER_VERSION, ZT_VERSION @@ -392,6 +393,15 @@ def test_read_message_last_unread_message_focused(self, mocker, [message_fixture['id']]) +class TestStreamsViewDivider: + def test_init(self): + streams_view_divider = StreamsViewDivider() + + assert isinstance(streams_view_divider, Divider) + assert streams_view_divider.stream_id == -1 + assert streams_view_divider.stream_name == '' + + class TestStreamsView: @pytest.fixture @@ -412,6 +422,7 @@ def test_init(self, mocker, stream_view): stream_view, 'SEARCH_STREAMS', stream_view.update_streams) @pytest.mark.parametrize('new_text, expected_log, to_pin', [ + # NOTE: '' represents StreamsViewDivider's stream name. ('f', ['fan', 'FOO', 'foo', 'FOOBAR'], []), ('bar', ['bar'], []), ('foo', ['FOO', 'foo', 'FOOBAR'], []), @@ -420,8 +431,10 @@ def test_init(self, mocker, stream_view): ('here', ['test here'], []), ('test here', ['test here'], []), # With 'foo' pinned. - ('f', ['foo', 'fan', 'FOO', 'FOOBAR'], [['foo'], ]), - ('FOO', ['foo', 'FOO', 'FOOBAR'], [['foo'], ]), + ('f', ['foo', '', 'fan', 'FOO', 'FOOBAR'], [['foo'], ]), + ('FOO', ['foo', '', 'FOO', 'FOOBAR'], [['foo'], ]), + # With 'bar' pinned. + ('bar', ['bar'], [['bar'], ]), ]) def test_update_streams(self, mocker, stream_view, new_text, expected_log, to_pin): @@ -1107,13 +1120,13 @@ def test_streams_view(self, mocker, streams, pinned, width=40): stream_button = mocker.patch(VIEWS + '.StreamButton') stream_view = mocker.patch(VIEWS + '.StreamsView') line_box = mocker.patch(VIEWS + '.urwid.LineBox') - divider = mocker.patch(VIEWS + '.urwid.Divider') + divider = mocker.patch(VIEWS + '.StreamsViewDivider') mocker.patch(STREAMBUTTON + ".mark_muted") left_col_view = LeftColumnView(width, self.view) if pinned: - divider.assert_called_once_with('-') + assert divider.called else: divider.assert_not_called() diff --git a/zulipterminal/ui_tools/views.py b/zulipterminal/ui_tools/views.py index 9f6ce3e41e..6ed50ffe8c 100644 --- a/zulipterminal/ui_tools/views.py +++ b/zulipterminal/ui_tools/views.py @@ -245,6 +245,18 @@ def read_message(self, index: int=-1) -> None: self.model.mark_message_ids_as_read(read_msg_ids) +class StreamsViewDivider(urwid.Divider): + """ + A custom urwid.Divider to visually separate pinned and unpinned streams. + """ + def __init__(self) -> None: + # FIXME: Necessary since the divider is treated as a StreamButton. + # NOTE: This is specifically for stream search to work correctly. + self.stream_id = -1 + self.stream_name = '' + super().__init__(div_char='-') + + class StreamsView(urwid.Frame): def __init__(self, streams_btn_list: List[Any], view: Any) -> None: self.view = view @@ -275,6 +287,25 @@ def update_streams(self, search_box: Any, new_text: str) -> None: ] streams_display = match_stream(stream_buttons, new_text, self.view.pinned_streams)[0] + + # Add a divider to separate pinned streams from the rest. + pinned_stream_names = [ + stream[0] + for stream in self.view.pinned_streams + ] + first_unpinned_index = len(streams_display) + for index, stream in enumerate(streams_display): + if stream.stream_name not in pinned_stream_names: + first_unpinned_index = index + break + if first_unpinned_index not in [0, len(streams_display)]: + # Do not add a divider when it is already present. This can + # happen when new_text=''. + if not isinstance(streams_display[first_unpinned_index], + StreamsViewDivider): + streams_display.insert(first_unpinned_index, + StreamsViewDivider()) + self.log.clear() self.log.extend(streams_display) self.view.controller.update_screen() @@ -696,14 +727,7 @@ def streams_view(self) -> Any: ) for stream in self.view.pinned_streams] if len(streams_btn_list): - unpinned_divider = urwid.Divider("-") - - # FIXME Necessary since the divider is treated as a StreamButton - # NOTE: This is specifically for stream search to work correctly - unpinned_divider.stream_id = -1 - unpinned_divider.stream_name = '' - - streams_btn_list += [unpinned_divider] + streams_btn_list += [StreamsViewDivider()] streams_btn_list += [ StreamButton(