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

[WIP] Disable cycling while editing a PM #1280

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

Subhasish-Behera
Copy link
Contributor

@Subhasish-Behera Subhasish-Behera commented Jan 4, 2023

What does this PR do?
-> Disables cycling during editing a PM by tab key .
-> Shows a warning if one uses tab during editing a PM.

Partial fix for #774

Tested?

  • Manually
  • Existing tests (adapted, if necessary)
  • New tests added (for any new behavior)
  • Passed linting & tests (each commit)

Commit flow

commit 1: changes made to boxes.py
commit 2: changes made to test_boxes.py
Notes & Questions

Interactions

Visual changes
zt1

@zulipbot zulipbot added the size: S [Automatic label added by zulipbot] label Jan 4, 2023
@Subhasish-Behera Subhasish-Behera changed the title Disable cycling pm Disable cycling while editing a PM Jan 4, 2023
@Subhasish-Behera Subhasish-Behera marked this pull request as ready for review January 4, 2023 14:58
@Subhasish-Behera
Copy link
Contributor Author

@zulipbot add "PR needs review"

@zulipbot zulipbot added the PR needs review PR requires feedback to proceed label Jan 10, 2023
Copy link
Collaborator

@neiljp neiljp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Subhasish-Behera Thanks for working on this too 👍

I've tested this manually and it seems to function well, for use of cycling using TAB or similar.

In the version we merged for streams, it benefits from the stream being a non-editable text object, which also limits clicking into the box or other accidental cases where it receives focus. That is a small issue with the solution in this PR right now, since we have only one private compose setup function.

Taking an approach like for streams but for private messages, seems like it would also avoid the possible need for tracking the reply state. The branch where we reuse the reply mode would then use a dedicated setup function for private edit, like we already have for stream_box_edit_view.

We could still maintain an error message for tab (or up) when editing a PM, as a separate commit/feature, though I don't think it's as important as making the recipient(s) unchangeable.

How does that sound?

zulipterminal/ui_tools/boxes.py Outdated Show resolved Hide resolved
zulipterminal/ui_tools/boxes.py Outdated Show resolved Hide resolved
@neiljp neiljp added area: UI General user interface update PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback and removed PR needs review PR requires feedback to proceed labels Jan 16, 2023
@zulipbot
Copy link
Member

Heads up @Subhasish-Behera, we just merged some commits that conflict with the changes you made in this pull request! You can review this repository's recent commits to see where the conflicts occur. Please rebase your feature branch against the upstream/main branch and resolve your pull request's merge conflicts accordingly.

@Subhasish-Behera Subhasish-Behera changed the title Disable cycling while editing a PM [WIP] Disable cycling while editing a PM Mar 16, 2023
@zulipbot zulipbot added size: XL [Automatic label added by zulipbot] and removed size: S [Automatic label added by zulipbot] labels Apr 16, 2023
@Subhasish-Behera Subhasish-Behera force-pushed the disable_cycling_PM branch 3 times, most recently from e304afe to f635f03 Compare April 16, 2023 16:06
@Subhasish-Behera
Copy link
Contributor Author

The new code dosen't need self.reply_keypressed or self.edit_keypressed to track which key triggered the private_box_view as you suggested.

@Subhasish-Behera
Copy link
Contributor Author

@zulipbot add "PR needs review"

@zulipbot zulipbot added the PR needs review PR requires feedback to proceed label Apr 16, 2023
Copy link
Collaborator

@neiljp neiljp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Subhasish-Behera This looks much different - and more detailed - and certainly seems to work in practice 👍

Your commit style is broadly OK, but there are a few elements that are throwing the style off:

  • there's no blank line between the title and summary (so tig is showing it all on one line for me!)
  • it's challenging, but it works best, including for github breaking the title into multiple lines, if you can keep the title to 72 characters
  • using gitlint as per the README should help prompt for these issues; if not then we can adjust the rules it uses perhaps
  • for referring to future work, it's useful to refer to the principle of why you're doing it, rather than eg. specific function names that don't yet exist :)

The tests are generally good to see, with a few points to bear in mind:

  • if adding a test for existing code, do it before modifying; you can then be more sure that you don't break anything
  • if adding a function, generally add the test in that commit too
  • it's not always necessary to test every underlying helper function
  • similarly, it's good to aim for the big picture behavior, though sometimes it can be challenging not to test the implementation

I like how you extracted the function to handle the common code, in addition to the one for the base PM/DM setup, though I do wonder if it would be simpler to incorporate it into the existing similar function.

I'll review the tests in more detail later, since we may wish to restructure again slightly before we're done, as per my comments - this is another reason that testing every little helper function can be a challenge, since it's testing the implementation in some sense!

Comment on lines -202 to -214
self.to_write_box = ReadlineEdit("To: ", edit_text=recipient_info)
self.to_write_box.enable_autocomplete(
func=self._to_box_autocomplete,
key=primary_key_for_command("AUTOCOMPLETE"),
key_reverse=primary_key_for_command("AUTOCOMPLETE_REVERSE"),
)
self.to_write_box.set_completer_delims("")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 202-208 definitely make sense to move out of the common method, based on the stream design.

Is the point here that the editing version will have typing updates of a different form, or something like that? So the other lines also move due to that reason?

This commit appears to be a refactor - please add that as an area if so - but my main query here is the order of operations from this refactoring. The deleted code is essentially moved to the top of private_box_view, so it would be worth mentioning that in the commit text.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood correctly, you are asking about lines other than 202 to 208.
In the overall implementation(beyond this particular commit), the plan is to not send typing updates at all for the editing version. Because we don't want to send typing notifications for editing a pre-existing message. so earlier the typing updates was sent for every kind of private writebox(reply,edit etc). but now it's not a part of the editing version.

The same thing is done behaviour-wise in the web app. Like when you edit a PM you won't see typing updates being a recipient of that message(similar to this PR's behaviour and unlike current upstream ZT's behaviour).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we shouldn't be sending typing updates for editing, I suspect that could be clearer in the API documentation. If you're sure this is the behavior we want, posting to that stream that you think that's the case would be useful, or even a quick PR for it.

Since we're splitting the private-compose and private-edit UI it certainly makes it simpler to separate this behavior in this way.

Comment on lines 208 to 211
def convert_id_to_info(
self, recipient_user_ids: Optional[List[int]] = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be better incorporated into _set_regular_and_typing_recipient_user_ids?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be possible if we were only talking about convert_id_to_info but _set_regular_and_typing_recipient_user_ids is also used by update_recipients function .

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so

  • _set_regular_and_typing_recipient_user_ids does the following, dependent upon passed user_ids
    • sets recipient_user_ids (autocompletion)
    • sets typing_recipient_user_ids (not including user)
  • update_recipients (which appears to effectively be update_recipients_from_emails_in_widget or similar):
    • sets recipient_emails
    • calls _set_regular_and_typing_recipient_user_ids with calculated ids from recipient_emails
  • convert_id_to_info does the following, dependent upon the passed user_ids
    • calls _set_regular_and_typing_recipient_user_ids
    • sets recipient_emails
    • sets recipient_info

This pattern suggests that the last two methods have a similar intent and so should share a name prefix, and generally could benefit from renaming.

The latter sets an extra attribute, which seems to be for initialization only - can we make that a return value instead?

Comment on lines +1085 to +1086
self.model.controller.view.write_box.private_box_edit_view(
recipient_user_ids=self.recipient_ids,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what enables the new edit-private-message functionality, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! Just like in the case of "REPLY_MESSAGE" keypress, but here calls to a private_box_edit_view function.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid commit, but we might squash with the previous one(s), as this change is small and straightforward now :)

zulipterminal/ui_tools/boxes.py Outdated Show resolved Hide resolved
zulipterminal/ui_tools/boxes.py Outdated Show resolved Hide resolved
tests/ui_tools/test_boxes.py Outdated Show resolved Hide resolved
@neiljp
Copy link
Collaborator

neiljp commented Sep 9, 2023

@Subhasish-Behera Is this ready for another review?

Subhasish-Behera added 2 commits September 9, 2023 19:08
The new approach is similar to how streams setup the coompose box. Here
_setup_common_private_compose setsup the common requirements of diffrent
write_box view. Mainly the logic behind the self.to_write_box such as
it's auto-complete and when to send typing event updates(yes in reply state
and no in edit state) is not covered here as they are non-common.
Mainly tests if autocomplete has been called, connect_signal of urwid
as msg_write_box is editable and changes are expected.
@Subhasish-Behera
Copy link
Contributor Author

@Subhasish-Behera Is this ready for another review?

Yep.

@Subhasish-Behera Subhasish-Behera added the PR needs review PR requires feedback to proceed label Sep 11, 2023
Subhasish-Behera and others added 9 commits September 11, 2023 20:06
This function takes in recipient_user_ids and sets the recipient info
accordingly.This function ensures there will be no code repetition. Another
change is making the recipinet_info a class attribute as it will be used
in edit view, non edit view, converting id to info.
The test are based on size of recipient_user_ids list as well
as if it is empty or not.Then the code uses user_id_email_dict and
user_dict to create the end recipient_info.
It tests for autocomplete is called twice, once indirectly for
msg_write_box and other directly for to_write_box.Also asserts
if to_write_box is editable.
With this function, private messages compose boxes uses 2 different
functions for WriteBox while editing a message and replying a message.
Here the self.to_write_box is non-editable urwid.Text.Also as its not
editable, there is no need for auto-complete(unlike private_box_view).
Its also dosen't send typing event updates to other recipients as it
should be done for new messages and not for editing old messages.
It tests if enable_autocomplete is just called once indirectly for
msg_write_box (and not to_write_box).Also checks if to_write_box is
non-editable.
Earlier editing was dependent on calling the reply_message keypress but
now it calls a separate method custom to creating a WriteBox for editing
private message.
The WriteBox has same keypress for the box created for editing as well
as reply. There is no neet to check the validity of self.to_write_box in
case of editing as it's non-editable.So now it checks the msg_edit_state
. Also now if the focus_position is FOCUS_CONTAINER_MESSAGE then the tab
will change the focus to FOCUS_CONTAINER_HEADER only if its a reply
(msg_edit_state =None).
Tests Adapted.
Change the name of update_recipients to
update_recipients_from_emails_in_widget.
Copy link
Collaborator

@neiljp neiljp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Subhasish-Behera This looks like it will be really useful! 👍

I did leave a lot of comments, but some relate to commit structure, commit text, and smaller changes or suggestions.

It seems like the functionality is essentially done, so this should be a case of moving things around and some final tidying, with some optional extensions.

Other than fixing the issue fully, this will be a good refactor to the compose code to have in place for future work! 🎉

*,
recipient_user_ids: Optional[List[int]] = None,
) -> None:
self.recipient_info = self.update_recipients_from_user_ids(recipient_user_ids)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.recipient_info doesn't appear to be used by any of your later commits, outside of the setup functions (this and the edit equivalent), so it seems like you can continue to use recipient_info instead?

Comment on lines +1758 to +1763
write_box.recipient_info = write_box.update_recipients_from_user_ids(
recipient_user_ids
)

assert write_box.recipient_emails == expected_recipient_emails
assert write_box.recipient_info == expected_recipient_info
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this isn't too likely to break anything, the assignment to write_box.recipient_info is related to the current implementation of the function that uses this new function (currently private_box_view, if we make that change in the earlier commit). This test should only care about what the function should achieve internally, including what it returns. The return value can be set to a local name in this test function - it doesn't need to be write_box.recipient_info.

This can be one of the challenges of writing tests for helper functions.

write_box.model.user_id_email_dict = user_id_email_dict
write_box.model.user_dict = user_dict
connect_signal_mock = mocker.patch.object(urwid, "connect_signal")
write_box.private_box_view(recipient_user_ids=recipient_user_ids)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Unless the test is only a few lines, please separate the line being tested from the setup and assert lines by blank lines, to make it easier to pick out which lines are which (you did this in the previous commit!)

Comment on lines +1718 to +1719
write_box.to_write_box = mocker.MagicMock(spec=ReadlineEdit)
enable_autocomplete = mocker.patch.object(ReadlineEdit, "enable_autocomplete")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_write_box is already a mock, so is the enable_autocomplete line necessary? It seems like it patches one part of the mock, but not to anything in particular, and returns that part. You do test that mocked/patched part, but I'm not sure it would be any different than

enable_autocomplete = write_box.to_write_box.enable_autocomplete

or alternatively substituting that inline where you have the assert_has_calls, since you only use/assert enable_autocomplete once in the test.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems rather that the previous line isn't necessary, as the write_box.to_write_box will be completely replaced by a new ReadlineEdit in this line.

So we need to patch ReadlineEdit here, but not patch to_write_box initially.

user_dict: List[Dict[str, Any]],
user_id_email_dict: Dict[int, str],
) -> None:
recipient_user_ids = [11]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest placing this as a default argument, but I'm not sure if this has the typical python issue with potential errors from persistent mutable default arguments. Typing it as a Mapping rather than Dict may help.

If we do this, that eases any parametrizing them later - and saves a blank line in this case ;)

]
self.focus_position = self.FOCUS_CONTAINER_MESSAGE

self._setup_common_private_compose()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To improve readability later, it may be useful to regroup lines in this function, since we're eventually going to have

  • the new user setup function
  • the custom UI header, leading into setting up the common UI part using the header
  • setting up the typing status code

In particular, we have part of the last bullet oddly above this line (send_next_typing_update) and not with the related code. Once the code is simplified, it'd also be good if that was naturally further down, and each group separated by a blank line.

In the edit version you can do any grouping when you introduce it; for this function I'll leave you to decide where to add/leave blank lines and move the line I mentioned, but eg. you delete a blank line in this commit, that could separate where the new function is from the 'timedelta' lines below.

Comment on lines +931 to +932
"Recipient(s) can not be edited while editing a",
"Direct Message.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing a space.

Also note that you don't need a comma (or 2) - the string literals will automatically join together.

# that all the recipients are valid, to avoid including any
# invalid ones.
self.update_recipients(self.to_write_box)
if self.msg_edit_state is None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this commit, the previous commit - using the new edit DM box, with different widgets - results in a crash when cycling with Tab, since it tries to validate the text and it has a different widget structure.

Since this and the next commit affect the Tab behavior only, while the earlier commits handle the editable nature of the to-box and splitting out the typing notifications, I'd suggest moving these commits to the front of the PR - these are independent groups of changes that don't have to be done in a specific order. That will make the Tab fixes come in first, followed by fixing the clickable To box, and changing the notifications behavior.

Comment on lines +1085 to +1086
self.model.controller.view.write_box.private_box_edit_view(
recipient_user_ids=self.recipient_ids,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid commit, but we might squash with the previous one(s), as this change is small and straightforward now :)

@@ -408,7 +408,7 @@ def test_footer_notification_on_invalid_recipients(
),
],
)
def test_update_recipients(
def test_update_recipients_from_emails_in_widget(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re commit title: good to see a refactor: here, but remember to include the file(s) as an area too. This should be clear from our README.

@neiljp neiljp removed the PR needs review PR requires feedback to proceed label Oct 5, 2023
@zulipbot
Copy link
Member

Heads up @Subhasish-Behera, we just merged some commits that conflict with the changes you made in this pull request! You can review this repository's recent commits to see where the conflicts occur. Please rebase your feature branch against the upstream/main branch and resolve your pull request's merge conflicts accordingly.

@neiljp neiljp added PR completion candidate PR with reviews that may unblock merging and removed PR needs mentor review labels May 8, 2024
@Niloth-p Niloth-p assigned zormit and unassigned zormit May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: UI General user interface update has conflicts PR awaiting update PR has been reviewed & is awaiting update or response to reviewer feedback PR completion candidate PR with reviews that may unblock merging PR replaced by another PR size: XL [Automatic label added by zulipbot]
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants