diff --git a/tests/ui/test_ui_tools.py b/tests/ui/test_ui_tools.py index 908fe4a4904..ce1ff289ea9 100644 --- a/tests/ui/test_ui_tools.py +++ b/tests/ui/test_ui_tools.py @@ -1318,7 +1318,7 @@ def test_private_message_to_self(self, mocker): mocker.patch.object(MessageBox, 'main_view') msg_box = MessageBox(message, self.model, None) - assert msg_box.recipients_emails == 'foo@zulip.com' + assert msg_box.recipient_emails == ['foo@zulip.com'] msg_box._is_private_message_to_self.assert_called_once_with() @pytest.mark.parametrize('content, markup', [ diff --git a/tests/ui_tools/test_boxes.py b/tests/ui_tools/test_boxes.py index 827f90bdee4..3755628092c 100644 --- a/tests/ui_tools/test_boxes.py +++ b/tests/ui_tools/test_boxes.py @@ -21,10 +21,11 @@ def mock_external_classes(self, mocker, initial_index): @pytest.fixture() def write_box(self, mocker, users_fixture, user_groups_fixture, - streams_fixture, unicode_emojis): + streams_fixture, unicode_emojis, user_dict): self.view.model.active_emoji_data = unicode_emojis write_box = WriteBox(self.view) write_box.view.users = users_fixture + write_box.model.user_dict = user_dict write_box.model.user_group_names = [ groups['name'] for groups in user_groups_fixture] @@ -589,7 +590,7 @@ def focus_val(x: str) -> int: if write_box.focus_position == write_box.FOCUS_CONTAINER_HEADER: write_box.header_write_box.focus_col = (focus_val( initial_focus_col_name)) - write_box.model.get_invalid_recipient_emails.return_value = [] + write_box.model.get_invalid_recipients.return_value = [] write_box.model.user_dict = mocker.MagicMock() write_box.keypress(size, tab_key) @@ -621,7 +622,7 @@ def test_write_box_header_contents(self, write_box, expected_box_size, elif msg_type == 'stream_edit': write_box.stream_box_edit_view(1000) else: - write_box.private_box_view(email='foo@gmail.com', + write_box.private_box_view(emails=['feedback@zulip.com'], recipient_user_ids=[1]) assert len(write_box.header_write_box.widget_list) == expected_box_size diff --git a/zulipterminal/model.py b/zulipterminal/model.py index a4d01d207e7..2ac9ebcc5fd 100644 --- a/zulipterminal/model.py +++ b/zulipterminal/model.py @@ -860,11 +860,12 @@ def _handle_typing_event(self, event: Event) -> None: else: raise RuntimeError("Unknown typing event operation") - def get_invalid_recipient_emails(self, recipient_emails: List[str] - ) -> List[str]: + def get_invalid_recipients(self, recipients: List[str]) -> List[str]: + recipients_list = ['{} <{}>'.format(self.user_dict[email]['full_name'], + email) for email in self.user_dict] - return [email for email in recipient_emails - if email not in self.user_dict] + return [recipient for recipient in recipients + if recipient not in recipients_list] def is_valid_stream(self, stream_name: str) -> bool: for stream in self.stream_dict.values(): diff --git a/zulipterminal/ui.py b/zulipterminal/ui.py index 2844368370f..1421abc5379 100644 --- a/zulipterminal/ui.py +++ b/zulipterminal/ui.py @@ -230,13 +230,13 @@ def keypress(self, size: Tuple[int, int], key: str) -> Optional[str]: stream_id=saved_draft['stream_id'], ) elif saved_draft['type'] == 'private': - email_txt = saved_draft['display_recipient'] + email_list = saved_draft['display_recipient'] recipient_user_ids = [ self.model.user_dict[email.strip()]['user_id'] - for email in email_txt.split(",") + for email in email_list ] self.write_box.private_box_view( - email=email_txt, + emails=email_list, recipient_user_ids=recipient_user_ids, ) content = saved_draft['content'] diff --git a/zulipterminal/ui_tools/boxes.py b/zulipterminal/ui_tools/boxes.py index 2742b961089..f3837bba809 100644 --- a/zulipterminal/ui_tools/boxes.py +++ b/zulipterminal/ui_tools/boxes.py @@ -109,20 +109,28 @@ def send_stop_typing_status(self) -> None: self.send_next_typing_update = datetime.now() self.idle_status_tracking = False - def private_box_view(self, *, email: str='', + def private_box_view(self, *, emails: Optional[List[str]]=None, recipient_user_ids: Optional[List[int]]=None) -> None: # Neither or both arguments should be set - assert ((email != '' and recipient_user_ids is not None) - or (email == '' and recipient_user_ids is None)) + assert ((emails is not None and recipient_user_ids is not None) + or (emails is None and recipient_user_ids is None)) self.set_editor_mode() - if recipient_user_ids: + recipient_info = '' + if recipient_user_ids and emails: self.recipient_user_ids = recipient_user_ids + self.recipient_emails = emails + for email in self.recipient_emails: + recipient_name = self.model.user_dict[email]['full_name'] + recipient_info += (', ' if recipient_info else '') + recipient_info += "{} <{}>".format(recipient_name, email) else: self.recipient_user_ids = [] + self.recipient_emails = [] self.send_next_typing_update = datetime.now() - self.to_write_box = ReadlineEdit("To: ", edit_text=email) + self.to_write_box = ReadlineEdit("To: ", + edit_text=recipient_info) self.msg_write_box = ReadlineEdit(multiline=True) self.msg_write_box.enable_autocomplete( func=self.generic_autocomplete, @@ -175,6 +183,12 @@ def track_idleness_and_update_status() -> None: urwid.connect_signal(self.msg_write_box, 'change', on_type_send_status) + def update_recipient_emails(self, write_box: ReadlineEdit) -> None: + self.recipient_emails = re.findall( + r'[\w\.-]+@[\w\.-]+', + write_box.edit_text + ) + def stream_box_view(self, stream_id: int, caption: str='', title: str='', ) -> None: self.set_editor_mode() @@ -466,10 +480,9 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: msg_id=self.msg_edit_id, ) else: - recipient_emails = [email.strip() for email in - self.to_write_box.edit_text.split(',')] + self.update_recipient_emails(self.to_write_box) success = self.model.send_private_message( - recipients=recipient_emails, + recipients=self.recipient_emails, content=self.msg_write_box.edit_text ) if success: @@ -487,8 +500,9 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: elif is_command_key('SAVE_AS_DRAFT', key): if not self.msg_edit_id: if self.to_write_box: + self.update_recipient_emails(self.to_write_box) message = Message( - display_recipient=self.to_write_box.edit_text, + display_recipient=self.recipient_emails, content=self.msg_write_box.edit_text, type='private', ) @@ -551,19 +565,22 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: else: header.focus_col = self.FOCUS_HEADER_BOX_STREAM else: - recipient_box = header[self.FOCUS_HEADER_BOX_RECIPIENT] - recipient_emails = [email.strip() for email in - recipient_box.edit_text.split(',')] - invalid_emails = self.model.get_invalid_recipient_emails( - recipient_emails) - if invalid_emails: - invalid_emails_error = ('Invalid recipient(s) - ' - + ', '.join(invalid_emails)) - self.view.set_footer_text(invalid_emails_error, 3) + self.update_recipient_emails(self.to_write_box) + recipients = [recipient_info.strip() for recipient_info + in self.to_write_box.edit_text.split(',')] + invalid_recipients = self.model.get_invalid_recipients( + recipients, + ) + if invalid_recipients: + invalid_recipients_error = ('Invalid recipient(s) - ' + + ', '.join( + invalid_recipients)) + self.view.set_footer_text(invalid_recipients_error, 3) return key users = self.model.user_dict self.recipient_user_ids = [users[email]['user_id'] - for email in recipient_emails] + for email in + self.recipient_emails] if not self.msg_body_edit_enabled: return key @@ -614,7 +631,7 @@ def __init__(self, message: Message, model: Any, if self._is_private_message_to_self(): recipient = self.message['display_recipient'][0] self.recipients_names = recipient['full_name'] - self.recipients_emails = self.model.user_email + self.recipient_emails = [self.model.user_email] self.recipient_ids = self.model.user_id else: self.recipients_names = ', '.join(list( @@ -622,11 +639,10 @@ def __init__(self, message: Message, model: Any, for recipient in self.message['display_recipient'] if recipient['email'] != self.model.user_email )) - self.recipients_emails = ', '.join(list( - recipient['email'] - for recipient in self.message['display_recipient'] - if recipient['email'] != self.model.user_email - )) + self.recipient_emails = [recipient['email'] for recipient in + self.message['display_recipient'] + if recipient['email'] + != self.model.user_email] self.recipient_ids = [recipient['id'] for recipient in self.message['display_recipient'] if recipient['id'] != self.model.user_id] @@ -1251,7 +1267,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: if is_command_key('ENTER', key): if self.message['type'] == 'private': self.model.controller.view.write_box.private_box_view( - email=self.recipients_emails, + emails=self.recipient_emails, recipient_user_ids=self.recipient_ids, ) elif self.message['type'] == 'stream': @@ -1263,7 +1279,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: elif is_command_key('STREAM_MESSAGE', key): if self.message['type'] == 'private': self.model.controller.view.write_box.private_box_view( - email=self.recipients_emails, + emails=self.recipient_emails, recipient_user_ids=self.recipient_ids, ) elif self.message['type'] == 'stream': @@ -1301,7 +1317,7 @@ def keypress(self, size: urwid_Size, key: str) -> Optional[str]: elif is_command_key('REPLY_AUTHOR', key): # All subscribers from recipient_ids are not needed here. self.model.controller.view.write_box.private_box_view( - email=self.message['sender_email'], + emails=[self.message['sender_email']], recipient_user_ids=[self.message['sender_id']], ) elif is_command_key('MENTION_REPLY', key): diff --git a/zulipterminal/ui_tools/buttons.py b/zulipterminal/ui_tools/buttons.py index d43e07c9cfb..8dcb4235f47 100644 --- a/zulipterminal/ui_tools/buttons.py +++ b/zulipterminal/ui_tools/buttons.py @@ -238,7 +238,7 @@ def _narrow_with_compose(self, button: Any) -> None: self.controller.narrow_to_user(self) self._view.body.focus.original_widget.set_focus('footer') self._view.write_box.private_box_view( - email=self.email, + emails=[self.email], recipient_user_ids=[self.user_id] )