Skip to content

Commit 179d6c3

Browse files
committed
views: Add search logic and full emoji list to EmojiPickerView.
This commit completes the logic of searching for emojis from the EmojiPickerView on pressing 'p' key. The search is kept kept dynamic for faster reacting to messages. The emoji list also displays the full list of emoji's present in the model.active_emoji_data. Tests amended.
1 parent 7520425 commit 179d6c3

File tree

6 files changed

+92
-22
lines changed

6 files changed

+92
-22
lines changed

tests/ui_tools/test_popups.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,13 +739,42 @@ class TestEmojiPickerView:
739739
@pytest.fixture(autouse=True)
740740
def mock_external_classes(self, mocker, monkeypatch, message_fixture):
741741
self.controller = mocker.Mock()
742+
self.view = self.controller.view
742743
mocker.patch.object(self.controller, 'maximum_popup_dimensions',
743744
return_value=(64, 64))
744745
mocker.patch(VIEWS + '.urwid.SimpleFocusListWalker', return_value=[])
745746
self.emoji_picker_view = EmojiPickerView(self.controller, 'ADD EMOJI',
746-
list(), message_fixture)
747+
list(), message_fixture,
748+
self.view)
747749
self.emoji_picker_view.allow_update_emoji_list = True
748750

751+
@pytest.mark.parametrize('emoji_names', [
752+
('action', 'agent', 'angel', 'anger', 'angry', 'engineer',
753+
'embarrassed', 'eye', 'ball', 'cat', 'hi', 'lightning',
754+
'smile', 'smiley', 'smirk', 'smoking')
755+
])
756+
@pytest.mark.parametrize(['search_string', 'assert_list',
757+
'match_return_value'], [
758+
('e', ['engineer', 'embarrassed', 'eye'], True),
759+
('sm', ['smile', 'smiley', 'smirk', 'smoking'], True),
760+
('ang', ['angel', 'anger', 'angry'], True),
761+
('abc', [], False),
762+
('q', [], False),
763+
])
764+
def test_update_emoji_list(self, mocker, emoji_names, search_string,
765+
assert_list, match_return_value):
766+
self.emoji_picker_view.emoji_buttons = (
767+
self.emoji_picker_view.generate_emoji_buttons(emoji_names))
768+
list_w = mocker.patch(VIEWS + ".urwid.SimpleFocusListWalker")
769+
770+
self.emoji_picker_view.update_emoji_list("SEARCH_EMOJIS",
771+
search_string)
772+
self.emojis_display = self.emoji_picker_view.emojis_display
773+
self.emojis_display = [
774+
emoji.emoji_name for emoji in self.emojis_display]
775+
assert self.emojis_display == assert_list
776+
assert self.emoji_picker_view.get_focus() == 'header'
777+
749778
@pytest.mark.parametrize('key', keys_for_command('SEARCH_EMOJIS'))
750779
def test_keypress_search_emoji(self, key, mocker, widget_size):
751780
size = widget_size(self.emoji_picker_view)
@@ -761,10 +790,11 @@ def test_keypress_exit_called(self, key, mocker, widget_size,
761790
if allow_update:
762791
assert self.controller.exit_popup.called
763792
else:
764-
assert self.emoji_picker_view.get_focus() == 'body'
793+
assert self.emoji_picker_view.get_focus() == 'header'
765794

766795
def test_keypress_navigation(self, mocker, widget_size,
767796
navigation_key_expected_key_pair):
797+
self.emoji_picker_view.set_focus('body')
768798
key, expected_key = navigation_key_expected_key_pair
769799
size = widget_size(self.emoji_picker_view)
770800
super_keypress = mocker.patch(VIEWS + '.urwid.ListBox.keypress')

zulipterminal/core.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ def show_msg_info(self, msg: Message,
216216

217217
def show_emoji_picker(self, message: Message) -> None:
218218
emoji_picker_view = EmojiPickerView(self, "Add/Remove reactions",
219-
['thumbs_up'], message)
219+
list(self.model.active_emoji_data.
220+
keys()),
221+
message, self.view)
220222
self.show_pop_up(emoji_picker_view, 'area:msg')
221223

222224
def show_stream_info(self, stream_id: int) -> None:

zulipterminal/model.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,6 @@ def _start_presence_updates(self) -> None:
323323
def react_to_message(self,
324324
message: Message,
325325
reaction_to_toggle: str) -> None:
326-
# FIXME Only support thumbs_up for now
327-
assert reaction_to_toggle == 'thumbs_up'
328326
assert reaction_to_toggle in self.active_emoji_data
329327

330328
reaction_to_toggle_spec = dict(

zulipterminal/ui_tools/boxes.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,14 +1564,18 @@ class PanelSearchBox(urwid.Edit):
15641564
"""
15651565

15661566
def __init__(self, panel_view: Any, search_command: str,
1567-
update_function: Callable[..., None]) -> None:
1567+
update_function: Callable[..., None],
1568+
fixed_search_caption: bool=False) -> None:
15681569
self.panel_view = panel_view
15691570
self.search_command = search_command
15701571
self.search_text = (
15711572
f"Search [{', '.join(keys_for_command(search_command))}]: "
15721573
)
15731574
urwid.connect_signal(self, 'change', update_function)
1574-
super().__init__(caption='', edit_text=self.search_text)
1575+
if not fixed_search_caption:
1576+
super().__init__(caption='', edit_text=self.search_text)
1577+
else:
1578+
super().__init__(caption=self.search_text, edit_text='')
15751579

15761580
def reset_search_text(self) -> None:
15771581
self.set_caption('')
@@ -1602,6 +1606,10 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
16021606
self.panel_view.view.controller.exit_editor_mode()
16031607
self.set_caption([('filter_results', 'Search Results'), ' '])
16041608
self.panel_view.set_focus("body")
1605-
if hasattr(self.panel_view, 'log') and len(self.panel_view.log):
1609+
if hasattr(self.panel_view, 'log') and (
1610+
(isinstance(self.panel_view.log, list)
1611+
and len(self.panel_view.log)) or (
1612+
isinstance(self.panel_view.log, urwid.ListBox)
1613+
and self.panel_view.body.get_focus()[0])):
16061614
self.panel_view.body.set_focus(0)
16071615
return super().keypress(size, key)

zulipterminal/ui_tools/buttons.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def update_count(self, count: int, text_color: Optional[str]=None) -> None:
6767
count_text = str(count)
6868
self.update_widget(('unread_count', count_text), new_color)
6969

70-
def update_widget(self, count_text: Tuple[str, str],
70+
def update_widget(self, count_text: Tuple[Optional[str], str],
7171
text_color: Optional[str]) -> Any:
7272
# Note that we don't modify self._caption
7373
max_caption_length = (self.width_for_text_and_count
@@ -284,15 +284,15 @@ def __init__(self, controller: Any, width: int, emoji_name: str,
284284
show_function=controller.model.toggle_message_reaction,
285285
width=width)
286286
if self.user_has_reacted_to_msg():
287-
self.update_widget(('bar', f' {CHECK_MARK} '), 'bar')
287+
self.update_widget((None, f' {CHECK_MARK} '), None)
288288

289289
def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
290290
if is_command_key('ENTER', key):
291291
# Note that this is called before toggle_message_reaction.
292292
if self.user_has_reacted_to_msg():
293-
self.update_widget(('bar', ''), 'bar')
293+
self.update_widget((None, ''), None)
294294
else:
295-
self.update_widget(('bar', f' {CHECK_MARK} '), 'bar')
295+
self.update_widget((None, f' {CHECK_MARK} '), None)
296296
return super().keypress(size, key)
297297

298298
def user_has_reacted_to_msg(self) -> bool:

zulipterminal/ui_tools/views.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
Message,
2727
asynch,
2828
edit_mode_captions,
29+
match_emoji,
2930
match_stream,
3031
match_user,
3132
)
@@ -1464,47 +1465,78 @@ class EmojiPickerView(PopUpView):
14641465
Displays Emoji Picker view for messages.
14651466
"""
14661467
def __init__(self, controller: Any, title: str, emoji_names: List[str],
1467-
message: Message) -> None:
1468+
message: Message, view: Any) -> None:
14681469
self.width = 40
1470+
self.view = view
14691471
self.message = message
14701472
self.controller = controller
1471-
emoji_buttons = self.generate_emoji_buttons(emoji_names)
1473+
self.emoji_buttons = self.generate_emoji_buttons(emoji_names)
14721474
self.emoji_search = PanelSearchBox(self,
14731475
'SEARCH_EMOJIS',
1474-
self.update_emoji_list)
1476+
self.update_emoji_list,
1477+
fixed_search_caption=True)
1478+
self.view.emoji_search = self.emoji_search
14751479
search_box = urwid.LineBox(
14761480
self.emoji_search, tlcorner='─', tline='', lline='',
14771481
trcorner='─', blcorner='─', rline='',
14781482
bline='─', brcorner='─'
14791483
)
1480-
self.allow_update_emoji_list = True
1481-
self.search_lock = threading.Lock()
1482-
super().__init__(controller, emoji_buttons, 'ADD_REACTION',
1484+
super().__init__(controller, self.emoji_buttons, 'ADD_REACTION',
14831485
self.width, title, header=[search_box])
1486+
self.set_focus('header')
1487+
self.allow_update_emoji_list = False
14841488

14851489
@asynch
14861490
def update_emoji_list(self, search_box: Any=None,
14871491
new_text: Optional[str]=None,
14881492
emoji_list: Any=None) -> None:
1489-
pass
1493+
"""
1494+
Updates emoji list via PanelSearchBox.
1495+
"""
1496+
assert (
1497+
(emoji_list is None and search_box is not None)
1498+
or (emoji_list is not None and search_box is None
1499+
and new_text is None)
1500+
)
1501+
1502+
if new_text:
1503+
self.emojis_display = [
1504+
searched_emoji for searched_emoji in self.emoji_buttons
1505+
if match_emoji(searched_emoji.emoji_name, new_text)
1506+
]
1507+
else:
1508+
self.emojis_display = self.emoji_buttons
1509+
1510+
self.contents['body'] = (urwid.ListBox(
1511+
urwid.SimpleFocusListWalker(
1512+
self.emojis_display)), None)
1513+
self.controller.update_screen()
14901514

14911515
def generate_emoji_buttons(self, emoji_names: List[str]
14921516
) -> List[EmojiButton]:
1493-
return [EmojiButton(self.controller, self.width, emoji_name,
1494-
self.message)
1495-
for emoji_name in emoji_names]
1517+
emoji_buttons = []
1518+
for index, emoji_name in enumerate(emoji_names):
1519+
emoji_buttons.append(urwid.AttrWrap(EmojiButton(
1520+
self.controller, self.width, emoji_name, self.message
1521+
), None if index % 2 else 'popup_contrast'))
1522+
return emoji_buttons
14961523

14971524
def keypress(self, size: urwid_Size, key: str) -> str:
14981525
if (is_command_key('SEARCH_EMOJIS', key)
14991526
and self.allow_update_emoji_list):
15001527
self.allow_update_emoji_list = False
15011528
self.emoji_search.set_edit_text('')
1529+
self.emoji_search.set_caption(
1530+
f"Search [{', '.join(keys_for_command('SEARCH_EMOJIS'))}]: ")
15021531
self.set_focus('header')
15031532
return key
15041533
elif (is_command_key('GO_BACK', key)
15051534
and not self.allow_update_emoji_list):
15061535
self.emoji_search.reset_search_text()
15071536
self.allow_update_emoji_list = True
1537+
self.contents['body'] = (
1538+
urwid.ListBox(urwid.SimpleFocusListWalker(
1539+
self.emoji_buttons)), None)
15081540
self.set_focus('body')
15091541
self.controller.update_screen()
15101542
return key

0 commit comments

Comments
 (0)