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

Render Spoilers in ZT #1529

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

Render Spoilers in ZT #1529

wants to merge 9 commits into from

Conversation

rsashank
Copy link
Member

@rsashank rsashank commented Jul 2, 2024

What does this PR do, and why?

Add support for rendering spoilers in messages:

  • Add styles for the spoiler header in themes.
  • Add spoiler processing to soup2markup.
  • Trim spoiler content to 10 characters, appending ... to ensure the table doesn't break.
  • Add SpoilerButton class, append spoiler buttons to the message info view.
  • Add SpoilerView class to view spoiler content.
  • Esc and Enter returns to the message info popup; i returns to the message view.

External discussion & connections

How did you test this?

  • Manually - Behavioral changes
  • Manually - Visual changes
  • Adapting existing automated tests
  • Adding automated tests for new behavior (or missing tests)
  • Existing automated tests should already cover this (only a refactor of tested code)

Self-review checklist for each commit

  • It is a minimal coherent idea
  • It has a commit summary following the documented style (title & body)
  • It has a commit summary describing the motivation and reasoning for the change
  • It individually passes linting and tests
  • It contains test additions for any new behavior
  • It flows clearly from a previous branch commit, and/or prepares for the next commit

Visual changes

Rendered Message Message Information Popup Spoiler View
image image image

@zulipbot zulipbot added the size: XL [Automatic label added by zulipbot] label Jul 2, 2024
@rsashank
Copy link
Member Author

rsashank commented Jul 2, 2024

Still working on this; I've created a draft PR if anyone wants to give feedback on it so far. :)

rsashank and others added 8 commits July 12, 2024 00:26
This commit:
 * adds styles for spoiler header in themes
 * adds markup entry in `soup2markup` for spoilers.

Tests added.

Co-authored-by: Preet Mishra <[email protected]>
Co-authored-by: Ezio-Sarthak <[email protected]>
This commit handles content metadata in `soup2markup` for spoilers.

Test amended.

Co-authored-by: Preet Mishra <[email protected]>
Co-authored-by: Ezio-Sarthak <[email protected]>
This commit adds popup to show hidden spoiler content.

Tests added.

Co-authored-by: Preet Mishra <[email protected]>
Co-authored-by: Ezio-Sarthak <[email protected]>
Tests amended.

Co-authored-by: Preet Mishra <[email protected]>
Co-authored-by: Ezio-Sarthak <[email protected]>
This commit adds a button that would allow user to view the spoiler
content, i.e., onclick of button is SpoilerView popup.

Tests added.

Co-authored-by: Preet Mishra <[email protected]>
Co-authored-by: Ezio-Sarthak <[email protected]>
This commit appends the SpoilerButton to message info view
if present.

Co-authored-by: Preet Mishra <[email protected]>
Co-authored-by: Ezio-Sarthak <[email protected]>
This commit:
 * Pass message info data to SpoilerButton and show_spoiler method.
 * Pass message info data to SpoilerView and return to message info
   view popup upon `Esc` and `Enter`.
 * Extract spoiler buttons processing to a create_spoiler_buttons.

Tests amended.
This commit sets show_footlink to False for links in
spoiler blocks.
This commit:
 * Adds a boolean value spoiler_link to metadata, indicating
   whether a link is part of spoiler content.
 * Adds [spoiler] to link caption if spoiler_link is True.

Fixes zulip#688.
Comment on lines +651 to +653
# Limit to the first 10 characters and append "..."
if len(processed_header_text) > 10:
processed_header_text = processed_header_text[:10] + "..."
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you have a literal like 10, it's almost always useful to give it a name, particularly when it's referenced in multiple locations like here. Other than being descriptive, it ensures everything gets updated at the same time, which reduces one bug location.

@Niloth-p
Copy link
Collaborator

I tested this.
It looks pretty cool. I really like the green "Spoiler:" text in zt_dark and zt_blue.
Looks fine in gruvbox_dark. I tried testing the light themes, but I gave up when my eyes started hurting.
I see that you've chosen to use one of the existing colors for all the themes, did you try out any other colors?

When quoting spoilers, the lines are offset (as expected).
Just mentioning this because I didn't notice it elsewhere in CZO or in the previous PR's comments.
Screenshot from 2024-07-18 14-40-47
And it gets more offset with more levels of depth, but that's probably quite a rare use case.
The quoted spoilers are also shown in the msg info view, so that's great.

Do we want to consider adding any formatting to the Spoiler View, to separate the header and the content? It looks quite fine now, no doubt, I'm only thinking about aesthetics.

But the "up/down scrolls" in the header felt a bit out of place to me. I appreciate sticking to the convention. But, could we update to include that only when not all of the spoiler is visible, and not show it otherwise?

Multiple spoilers, message links, opening/closing work fine.
Handles empty spoilers just like the webapp does, which is displaying an empty message 🤔. This is good for the PR. But I'm surprised. Maybe, as a followup, we can check for empty spoilers/quotes/links/code blocks/etc before sending messages. Not sure if there are discussions elsewhere regarding this.

Will review the code soon.

@Niloth-p Niloth-p mentioned this pull request Jul 19, 2024
18 tasks
Copy link
Collaborator

@Niloth-p Niloth-p left a comment

Choose a reason for hiding this comment

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

Lgtm.

All the passing around of arguments makes this quite bulky, I've opened #1537 which was discussed during #1455 and should help significantly simplify these PRs.

It would help if the commit descriptions of the 2nd and the 5th commits were re-written.
I think you can remove the commit description of the 6th and the 8th commits.

I didn't look into the header character count limits.

self.spoiler_view.keypress(size, key)
assert not self.controller.exit_popup.called

@pytest.mark.parametrize("key", {*keys_for_command("EXIT_POPUP")})
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
@pytest.mark.parametrize("key", {*keys_for_command("EXIT_POPUP")})
@pytest.mark.parametrize("key", keys_for_command("EXIT_POPUP"))

self,
mocker: MockerFixture,
widget_size: Callable[[Widget], urwid_Size],
navigation_key_expected_key_pair: Tuple[str, str] = ("ENTER", "ENTER"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume the intention is to clarify the variables, but is this really required for this test? Can't we just use a single variable?

@@ -481,6 +482,11 @@ def report_warning(
"""
self.view.set_footer_text(text, "task:warning", duration)

def show_spoiler(self, content: str) -> None:
self.show_pop_up(
SpoilerView(self, "Spoiler (up/down scrolls)", content), "area:msg"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I appreciate the convention. But, this suggestion of "up/down scrolls" looks out of place for most spoiler use cases. Could we update to include that only when not all of the spoiler is visible, and not show it otherwise?

if len(content_contents) == 1 and content_contents[0] == "\n":
content.contents.pop(0)

# FIXME: Do not soup2markup content in the MessageBox as it
Copy link
Collaborator

Choose a reason for hiding this comment

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

The comment seems outdated.

# Spoiler content
content = element.find(class_="spoiler-content")

# Remove surrounding newlines.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's change the variable name?
This code seems to make several non-trivial and effective assumptions about the data being passed to it through spoiler-content. Personally, I think improving the readability of this block can help.

  • Spoiler contents without headers - use the default heading 'Spoiler' which looks fine.
  • Spoiler headers without content - show empty spoiler content - should these be considered spoilers?
    But that's also in the same ballpark as allowing empty spoiler messages, so it's consistent.

) -> None:
self.selectable_icon = mocker.patch(MODULE + ".urwid.SelectableIcon")

# The method update_widget() is called in SpoilerButton's init.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure about this suggestion.
But, can we mix these 2 test functions since they're both having the same line of code tested? Just different assertions.
Removing the mocking of update_widget in the test_init, and adding a parameter set with None should work?

if text:
caption = f"{link_index}: {text}\n{link}"
if spoiler_link:
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 thinking of several different approaches like prepending the spoiler keyword to the text, then removing it later from text, or using negative integers to communicate spoiler links or such, but yeah, they're all worse solutions than passing around an extra bool value.

With #1537, this should become simplified, as you won't have to add the 'bool' type in so many different places. Even temporarily, you could just create a type and update it in one place.

Also, I only noticed later that most of the bulk comes from the 'bool' type and not the bool value. So, this should be fine after a bit of refactoring.

I didn't look deeply into this, hoping you can just tell me, do both message_links and topic_links necessarily need to be of the same type?

@mounilKshah
Copy link
Collaborator

Thanks @rsashank for working on this. I tried running this on my local and it worked fine. Apart from the suggestions already given, I don't have much to add for the current iteration of the PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for spoilers
5 participants