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

Handle topic links in message info view #867

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions tests/model/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,34 @@ def test_success_get_messages(self, mocker, messages_successful_response,
assert model.index['pointer'][repr(model.narrow)] == anchor
assert model._have_last_message[repr(model.narrow)] is True

@pytest.mark.parametrize('messages, expected_messages_response', [(
{'topic_links': [{'url': 'www.foo.com', 'text': 'Bar'}]},
{'topic_links': [{'url': 'www.foo.com', 'text': 'Bar'}]},
), (
{'topic_links': ['www.foo1.com']},
{'topic_links': [{'url': 'www.foo1.com', 'text': ''}]},
), (
{'topic_links': []},
{'topic_links': []},
), (
{'subject_links': ['www.foo2.com']},
{'topic_links': [{'url': 'www.foo2.com', 'text': ''}]},
), (
{'subject_links': []},
{'topic_links': []},
)], ids=[
'Zulip_4.0+_ZFL46_response_with_topic_links',
'Zulip_3.0+_ZFL1_response_with_topic_links',
'Zulip_3.0+_ZFL1_response_empty_topic_links',
'Zulip_2.1+_response_with_subject_links',
'Zulip_2.1+_response_empty_subject_links',
])
def test_modernize_message_response(self, model, messages,
expected_messages_response):

assert (model.modernize_message_response(messages)
== expected_messages_response)

def test_get_message_false_first_anchor(
self, mocker, messages_successful_response, index_all_messages,
initial_data, num_before=30, num_after=10
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/test_ui_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,8 @@ def test_init(self, mocker, message_type, set_fields):
assert msg_box.last_message == defaultdict(dict)
for field, invalid_default in set_fields:
assert getattr(msg_box, field) != invalid_default
if message_type == 'stream':
assert msg_box.topic_links == OrderedDict()
assert msg_box.message_links == OrderedDict()
assert msg_box.time_mentions == list()

Expand Down
54 changes: 52 additions & 2 deletions tests/ui_tools/test_popups.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def mock_external_classes(self, mocker):
self.edit_history_view = EditHistoryView(
controller=self.controller,
message=self.message,
topic_links=OrderedDict(),
message_links=OrderedDict(),
time_mentions=list(),
title='Edit History',
Expand All @@ -215,6 +216,7 @@ def mock_external_classes(self, mocker):
def test_init(self):
assert self.edit_history_view.controller == self.controller
assert self.edit_history_view.message == self.message
assert self.edit_history_view.topic_links == OrderedDict()
assert self.edit_history_view.message_links == OrderedDict()
assert self.edit_history_view.time_mentions == list()
self.controller.model.fetch_message_history.assert_called_once_with(
Expand Down Expand Up @@ -246,6 +248,7 @@ def test_keypress_show_msg_info(self, key, widget_size):

self.controller.show_msg_info.assert_called_once_with(
msg=self.message,
topic_links=OrderedDict(),
message_links=OrderedDict(),
time_mentions=list(),
)
Expand Down Expand Up @@ -450,7 +453,13 @@ def mock_external_classes(self, mocker, monkeypatch, message_fixture):
]
self.msg_info_view = MsgInfoView(self.controller, message_fixture,
'Message Information', OrderedDict(),
list())
OrderedDict(), list())

def test_init(self, message_fixture):
assert self.msg_info_view.msg == message_fixture
assert self.msg_info_view.topic_links == OrderedDict()
assert self.msg_info_view.message_links == OrderedDict()
assert self.msg_info_view.time_mentions == list()

def test_keypress_any_key(self, widget_size):
key = "a"
Expand Down Expand Up @@ -482,6 +491,7 @@ def test_keypress_edit_history(self, message_fixture, key, widget_size,
}
msg_info_view = MsgInfoView(self.controller, message_fixture,
title='Message Information',
topic_links=OrderedDict(),
message_links=OrderedDict(),
time_mentions=list())
size = widget_size(msg_info_view)
Expand All @@ -491,6 +501,7 @@ def test_keypress_edit_history(self, message_fixture, key, widget_size,
if msg_info_view.show_edit_history_label:
self.controller.show_edit_history.assert_called_once_with(
message=message_fixture,
topic_links=OrderedDict(),
message_links=OrderedDict(),
time_mentions=list(),
)
Expand Down Expand Up @@ -552,11 +563,50 @@ def test_height_reactions(self, message_fixture, to_vary_in_each_message):
varied_message = dict(message_fixture, **to_vary_in_each_message)
self.msg_info_view = MsgInfoView(self.controller, varied_message,
'Message Information', OrderedDict(),
list())
OrderedDict(), list())
# 9 = 3 labels + 1 blank line + 1 'Reactions' (category) + 4 reactions.
expected_height = 9
assert self.msg_info_view.height == expected_height

@pytest.mark.parametrize([
'initial_link',
'expected_text',
'expected_attr_map',
'expected_focus_map',
'expected_link_width'
], [(
OrderedDict([('https://bar.com', ('Foo', 1, True))]),
'1: Foo\nhttps://bar.com',
{None: 'popup_contrast'},
{None: 'selected'},
15,
), (
OrderedDict([('https://foo.com', ('', 1, True))]),
'1: https://foo.com',
{None: 'popup_contrast'},
{None: 'selected'},
18,
)], ids=[
'link_with_link_text',
'link_without_link_text',
]
)
def test_create_link_buttons(self, initial_link, expected_text,
expected_attr_map, expected_focus_map,
expected_link_width):
[link_w], link_width = self.msg_info_view.create_link_buttons(
self.controller, initial_link,
)

assert [link_w.link] == list(initial_link)
assert (link_w._wrapped_widget.original_widget.text
== expected_text)
assert (link_w._wrapped_widget.focus_map
== expected_focus_map)
assert (link_w._wrapped_widget.attr_map
== expected_attr_map)
assert link_width == expected_link_width

def test_keypress_navigation(self, mocker, widget_size,
navigation_key_expected_key_pair):
key, expected_key = navigation_key_expected_key_pair
Expand Down
7 changes: 6 additions & 1 deletion zulipterminal/api_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ class Message(TypedDict, total=False):
timestamp: int
client: str
subject: str # Only for stream msgs.
topic_links: List[str]
# NOTE: new in Zulip 3.0 / ZFL 1
# NOTE: API response format of `topic_links` new in Zulip 4.0 / ZFL 46
topic_links: List[Any]
# NOTE: `subject_links` new in Zulip 2.1;
# Deprecated from Zulip 3.0 / ZFL 1
subject_links: List[str]
is_me_message: bool
reactions: List[Dict[str, Any]]
submessages: List[Dict[str, Any]]
Expand Down
7 changes: 5 additions & 2 deletions zulipterminal/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,13 @@ def show_topic_edit_mode(self, button: Any) -> None:
self.show_pop_up(EditModeView(self, button), 'area:msg')

def show_msg_info(self, msg: Message,
topic_links: 'OrderedDict[str, Tuple[str, int, bool]]',
message_links: 'OrderedDict[str, Tuple[str, int, bool]]',
time_mentions: List[Tuple[str, str]],
) -> None:
msg_info_view = MsgInfoView(self, msg,
"Message Information (up/down scrolls)",
message_links, time_mentions)
topic_links, message_links, time_mentions)
self.show_pop_up(msg_info_view, 'area:msg')

def show_stream_info(self, stream_id: int) -> None:
Expand Down Expand Up @@ -241,11 +242,13 @@ def show_about(self) -> None:

def show_edit_history(
self, message: Message,
topic_links: 'OrderedDict[str, Tuple[str, int, bool]]',
message_links: 'OrderedDict[str, Tuple[str, int, bool]]',
time_mentions: List[Tuple[str, str]],
) -> None:
self.show_pop_up(
EditHistoryView(self, message, message_links, time_mentions,
EditHistoryView(self, message, topic_links,
message_links, time_mentions,
'Edit History (up/down scrolls)'),
'area:msg'
)
Expand Down
36 changes: 35 additions & 1 deletion zulipterminal/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,11 @@ def get_messages(self, *,
}
response = self.client.get_messages(message_filters=request)
if response['result'] == 'success':
response['messages'] = [
self.modernize_message_response(msg)
for msg in response['messages']
]

self.index = index_messages(response['messages'], self, self.index)
narrow_str = repr(self.narrow)
if first_anchor and response['anchor'] != 10000000000000000:
Expand All @@ -530,6 +535,35 @@ def get_messages(self, *,
display_error_if_present(response, self.controller)
return response['msg']

@staticmethod
def modernize_message_response(message: Message
) -> Message:
"""
Converts received message into the modern message response format.

This provides a good single place to handle support for older server
releases params, and making them compatible with recent releases.

TODO: This could be extended for other message params potentially
in future.
"""
# (1) `subject_links` param is changed to `topic_links` from
# feature level 1, server version 3.0
if 'subject_links' in message.keys():
message['topic_links'] = message.pop('subject_links')

# (2) Modernize `topic_links` old response (List[str]) to new response
# (List[Dict[str, str]])
if 'topic_links' in message.keys():
topic_links = [
{'url': link, 'text': ''} for link in message['topic_links']
if type(link) == str
]
if topic_links:
message['topic_links'] = topic_links

return message

def fetch_message_history(self, message_id: int,
) -> List[Dict[str, Union[int, str]]]:
"""
Expand Down Expand Up @@ -986,7 +1020,7 @@ def _handle_message_event(self, event: Event) -> None:
Handle new messages (eg. add message to the end of the view)
"""
assert event['type'] == "message"
message = event['message']
message = self.modernize_message_response(event['message'])
# sometimes `flags` are missing in `event` so initialize
# an empty list of flags in that case.
message['flags'] = event.get('flags', [])
Expand Down
11 changes: 11 additions & 0 deletions zulipterminal/ui_tools/boxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,13 +661,23 @@ def __init__(self, message: Message, model: 'Model',
self.message_links: 'OrderedDict[str, Tuple[str, int, bool]]' = (
OrderedDict()
)
self.topic_links: 'OrderedDict[str, Tuple[str, int, bool]]' = (
OrderedDict()
)
self.time_mentions: List[Tuple[str, str]] = list()
self.last_message = last_message
# if this is the first message
if self.last_message is None:
self.last_message = defaultdict(dict)

if self.message['type'] == 'stream':
# Set `topic_links` if present
for link in self.message.get('topic_links', []):
# Modernized response
self.topic_links[link['url']] = (
link['text'], len(self.topic_links) + 1, True
)

self.stream_name = self.message['display_recipient']
self.stream_id = self.message['stream_id']
self.topic_name = self.message['subject']
Expand Down Expand Up @@ -1549,6 +1559,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]:
self.model.controller.view.middle_column.set_focus('footer')
elif is_command_key('MSG_INFO', key):
self.model.controller.show_msg_info(self.message,
self.topic_links,
self.message_links,
self.time_mentions)
return key
Expand Down
Loading