Skip to content

Commit

Permalink
Merge pull request #76 from discord-modmail/fix/paginator-options
Browse files Browse the repository at this point in the history
fix: paginator can now not use an embed
  • Loading branch information
onerandomusername committed Dec 15, 2021
2 parents 7347f59 + 81b5692 commit d47e097
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 48 deletions.
78 changes: 59 additions & 19 deletions modmail/utils/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
JUMP_LAST_LABEL = " \u276f\u276f " # >>
STOP_PAGINATE_EMOJI = "\u274c" # [:x:] This is an emoji, which is treated differently from the above

NO_EMBED_FOOTER_BUMP = 15

_AUTOGENERATE = object()


logger: ModmailLogger = logging.getLogger(__name__)


Expand All @@ -57,13 +62,14 @@ def __init__(
contents: Union[List[str], str],
/,
source_message: Optional[discord.Message] = None,
embed: Embed = None,
embed: Union[Embed, bool, None] = _AUTOGENERATE,
timeout: float = 180,
*,
footer_text: str = None,
prefix: str = "```",
suffix: str = "```",
max_size: int = 2000,
title: str = None,
linesep: str = "\n",
only_users: Optional[List[Union[discord.Object, discord.abc.User]]] = None,
only_roles: Optional[List[Union[discord.Object, discord.Role]]] = None,
Expand All @@ -75,14 +81,33 @@ def __init__(
If source message is provided and only_users is NOT provided, the paginator will respond
to the author of the source message. To override this, pass an empty list to `only_users`.
By default, an embed is created. However, a custom embed can
be passed, or None can be passed to not use an embed.
"""
self.index = 0
self._pages: List[str] = []
self.prefix = prefix
self.suffix = suffix
self.max_size = max_size
self.linesep = linesep
self.embed = embed or Embed()
if embed is _AUTOGENERATE or embed is True:
self.embed = Embed()
else:
if embed is False:
embed = None
self.embed = embed

# used if embed is None
self.content = ""
if self.embed is None:
self.title = title
# need to set the max_size down a few to be able to set a "footer"
# page indicator is "page xx of xx"
self.max_size -= NO_EMBED_FOOTER_BUMP + len(self.title or "")
if self.title is not None:
self.max_size -= len(title)
if footer_text is not None:
self.max_size -= len(footer_text) + 1

# temporary to support strings as contents. This will be changed when we added wrapping.
if isinstance(contents, str):
Expand Down Expand Up @@ -116,8 +141,8 @@ def __init__(

# set footer to embed.footer if embed is set
# this is because we will be modifying the footer of this embed
if embed is not None:
if not isinstance(embed.footer, EmbedProxy) and footer_text is None:
if self.embed is not None:
if not isinstance(self.embed.footer, EmbedProxy) and footer_text is None:
footer_text = embed.footer
self.footer_text = footer_text
self.clear()
Expand All @@ -140,7 +165,7 @@ async def paginate(
source_message: discord.Message = None,
/,
timeout: float = 180,
embed: Embed = None,
embed: Embed = _AUTOGENERATE,
*,
footer_text: str = None,
only: Optional[discord.abc.User] = None,
Expand All @@ -149,6 +174,7 @@ async def paginate(
prefix: str = "",
suffix: str = "",
max_size: int = 4000,
title: str = None,
linesep: str = "\n",
only_users: Optional[List[Union[discord.Object, discord.abc.User]]] = None,
only_roles: Optional[List[Union[discord.Object, discord.abc.Role]]] = None,
Expand All @@ -167,6 +193,7 @@ async def paginate(
prefix=prefix,
suffix=suffix,
max_size=max_size,
title=title,
linesep=linesep,
only_users=only_users,
only_roles=only_roles,
Expand All @@ -178,18 +205,24 @@ async def paginate(
channel = source_message.channel

paginator.update_states()
paginator.embed.description = paginator.pages[paginator.index]
# if there's only one page, don't send the view
if len(paginator.pages) < 2:
await channel.send(embeds=[paginator.embed])
if paginator.embed:
await channel.send(embeds=[paginator.embed])
else:
await channel.send(content=paginator.content)

return

if len(paginator.pages) < (show_jump_buttons_min_pages or 3):
for item in paginator.children:
if getattr(item, "custom_id", None) in ["pag_jump_first", "pag_jump_last"]:
paginator.remove_item(item)

msg: discord.Message = await channel.send(embeds=[paginator.embed], view=paginator)
if paginator.embed is None:
msg: discord.Message = await channel.send(content=paginator.content, view=paginator)
else:
msg: discord.Message = await channel.send(embeds=[paginator.embed], view=paginator)

await paginator.wait()
await msg.edit(view=None)
Expand All @@ -212,15 +245,6 @@ async def interaction_check(self, interaction: Interaction) -> bool:
)
return False

def get_footer(self) -> str:
"""Returns the footer text."""
self.embed.description = self._pages[self.index]
page_indicator = f"Page {self.index+1}/{len(self._pages)}"
footer_txt = (
f"{self.footer_text} ({page_indicator})" if self.footer_text is not None else page_indicator
)
return footer_txt

def update_states(self) -> None:
"""
Disable specific components depending on paginator page and length.
Expand All @@ -230,7 +254,20 @@ def update_states(self) -> None:
if the paginator is on the last page, the jump last/move forward buttons will be disabled.
"""
# update the footer
self.embed.set_footer(text=self.get_footer())
page_indicator = f"Page {self.index+1}/{len(self._pages)}"
if self.footer_text:
footer_text = f"{self.footer_text} ({page_indicator})"
else:
footer_text = page_indicator

if self.embed is None:
self.content = (self.title or "") + "\n"
self.content += self._pages[self.index]
self.content += "\n" + footer_text

else:
self.embed.description = self._pages[self.index]
self.embed.set_footer(text=footer_text)

# determine if the jump buttons should be enabled
more_than_two_pages = len(self._pages) > 2
Expand Down Expand Up @@ -264,7 +301,10 @@ async def send_page(self, interaction: Interaction) -> None:
"""Send new page to discord, after updating the view to have properly disabled buttons."""
self.update_states()

await interaction.message.edit(embed=self.embed, view=self)
if self.embed:
await interaction.message.edit(embed=self.embed, view=self)
else:
await interaction.message.edit(content=self.content, view=self)

@ui.button(label=JUMP_FIRST_LABEL, custom_id="pag_jump_first", style=ButtonStyle.primary)
async def go_first(self, _: Button, interaction: Interaction) -> None:
Expand Down
29 changes: 0 additions & 29 deletions tests/modmail/utils/test_pagination.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List, Union

import pytest

from modmail.utils.pagination import ButtonPaginator
Expand All @@ -11,30 +9,3 @@ async def test_paginator_init() -> None:
content = ["content"]
paginator = ButtonPaginator(content, prefix="", suffix="", linesep="")
assert paginator.pages == content


@pytest.mark.asyncio
@pytest.mark.parametrize(
"content, footer_text",
[
(["5"], "Snap, crackle, pop"),
(["Earthly"], "world"),
("There are no plugins installed.", None),
],
)
async def test_paginator_footer(content: Union[str, List[str]], footer_text: str) -> None:
"""Test the paginator footer matches what is passed."""
pag = ButtonPaginator(content, footer_text=footer_text)
print("index:", pag.index)
print("page len: ", len(pag.pages))
assert pag.footer_text == footer_text
if isinstance(content, str):
content = [content]

print(pag.get_footer())
if footer_text is not None:
assert pag.get_footer().endswith(f"{len(content)})")
assert pag.get_footer().startswith(footer_text)

else:
assert pag.get_footer().endswith(f"{len(content)}")

0 comments on commit d47e097

Please sign in to comment.