Skip to content

Commit

Permalink
Merge pull request Pycord-Development#1123 from krittick/pages-update-2
Browse files Browse the repository at this point in the history
Add `Page` class to `ext.pages` to allow for greater flexibility with page contents
  • Loading branch information
krittick authored Mar 6, 2022
1 parent 085d693 commit 24f05ed
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 37 deletions.
123 changes: 86 additions & 37 deletions discord/ext/pages/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"Paginator",
"PageGroup",
"PaginatorMenu",
"Page",
)


Expand Down Expand Up @@ -104,6 +105,48 @@ async def callback(self, interaction: discord.Interaction):
await self.paginator.goto_page(page_number=self.paginator.current_page)


class Page:
"""Represents a page shown in the paginator.
Allows for directly referencing and modifying each page as a class instance.
Parameters
----------
content: :class:`str`
The content of the page. Corresponds to the :class:`discord.Message.content` attribute.
embeds: Optional[List[Union[List[:class:`discord.Embed`], :class:`discord.Embed`]]]
The embeds of the page. Corresponds to the :class:`discord.Message.embeds` attribute.
"""

def __init__(
self, content: Optional[str] = None, embeds: Optional[List[Union[List[discord.Embed], discord.Embed]]] = None
):
if content is None and embeds is None:
raise discord.InvalidArgument("A page cannot have both content and embeds equal to None.")
self._content = content
self._embeds = embeds

@property
def content(self) -> Optional[str]:
"""Gets the content for the page."""
return self._content

@content.setter
def content(self, value: Optional[str]):
"""Sets the content for the page."""
self._content = value

@property
def embeds(self) -> Optional[List[Union[List[discord.Embed], discord.Embed]]]:
"""Gets the embeds for the page."""
return self._embeds

@embeds.setter
def embeds(self, value: Optional[List[Union[List[discord.Embed], discord.Embed]]]):
"""Sets the embeds for the page."""
self._embeds = value


class PageGroup:
"""Creates a group of pages which the user can switch between.
Expand All @@ -116,8 +159,8 @@ class PageGroup:
Parameters
----------
pages: Union[List[:class:`str`], List[Union[List[:class:`discord.Embed`], :class:`discord.Embed]]]
The list of strings, embeds, or list of embeds to include in the page group.
pages: Union[List[:class:`str`], List[:class:`Page`], List[Union[List[:class:`discord.Embed`], :class:`discord.Embed]]]
The list of :class:`Page` objects, strings, embeds, or list of embeds to include in the page group.
label: :class:`str`
The label shown on the corresponding PaginatorMenu dropdown option.
Also used as the SelectOption value.
Expand Down Expand Up @@ -150,7 +193,7 @@ class PageGroup:

def __init__(
self,
pages: Union[List[str], List[Union[List[discord.Embed], discord.Embed]]],
pages: Union[List[str], List[Page], List[Union[List[discord.Embed], discord.Embed]]],
label: str,
description: str,
emoji: Union[str, discord.Emoji, discord.PartialEmoji] = None,
Expand Down Expand Up @@ -186,8 +229,8 @@ class Paginator(discord.ui.View):
Parameters
----------
pages: Union[List[:class:`PageGroup`], List[:class:`str`], List[Union[List[:class:`discord.Embed`], :class:`discord.Embed`]]]
The list of :class:`PageGroup` objects, strings, embeds, or list of embeds to paginate.
pages: Union[List[:class:`PageGroup`], List[:class:`Page`], List[:class:`str`], List[Union[List[:class:`discord.Embed`], :class:`discord.Embed`]]]
The list of :class:`PageGroup` objects, :class:`Page` objects, strings, embeds, or list of embeds to paginate.
If a list of :class:`PageGroup` objects is provided and `show_menu` is ``False``, only the first page group will be displayed.
show_disabled: :class:`bool`
Whether to show disabled buttons.
Expand Down Expand Up @@ -233,7 +276,7 @@ class Paginator(discord.ui.View):

def __init__(
self,
pages: Union[List[PageGroup], List[str], List[Union[List[discord.Embed], discord.Embed]]],
pages: Union[List[PageGroup], List[Page], List[str], List[Union[List[discord.Embed], discord.Embed]]],
show_disabled: bool = True,
show_indicator=True,
show_menu=False,
Expand All @@ -248,15 +291,19 @@ def __init__(
) -> None:
super().__init__(timeout=timeout)
self.timeout: float = timeout
self.pages: Union[List[PageGroup], List[str], List[Union[List[discord.Embed], discord.Embed]]] = pages
self.pages: Union[
List[PageGroup], List[str], List[Page], List[Union[List[discord.Embed], discord.Embed]]
] = pages
self.current_page = 0
self.menu: Optional[PaginatorMenu] = None
self.show_menu = show_menu
self.page_groups: Optional[List[PageGroup]] = None

if all(isinstance(pg, PageGroup) for pg in pages):
self.page_groups = self.pages if show_menu else None
self.pages: Union[List[str], List[Union[List[discord.Embed], discord.Embed]]] = self.page_groups[0].pages
self.pages: Union[
List[str], List[Page], List[Union[List[discord.Embed], discord.Embed]]
] = self.page_groups[0].pages

self.page_count = len(self.pages) - 1
self.buttons = {}
Expand Down Expand Up @@ -284,7 +331,7 @@ def __init__(

async def update(
self,
pages: Optional[Union[List[str], List[Union[List[discord.Embed], discord.Embed]]]] = None,
pages: Optional[Union[List[str], List[Page], List[Union[List[discord.Embed], discord.Embed]]]] = None,
show_disabled: Optional[bool] = None,
show_indicator: Optional[bool] = None,
author_check: Optional[bool] = None,
Expand All @@ -300,8 +347,8 @@ async def update(
Parameters
----------
pages: Optional[Union[List[:class:`PageGroup`], List[:class:`str`], List[Union[List[:class:`discord.Embed`], :class:`discord.Embed]]]]
The list of :class:`PageGroup` objects, strings, embeds, or list of embeds to paginate.
pages: Optional[Union[List[:class:`PageGroup`], List[:class:`Page`], List[:class:`str`], List[Union[List[:class:`discord.Embed`], :class:`discord.Embed]]]]
The list of :class:`PageGroup` objects, :class:`Page` objects, strings, embeds, or list of embeds to paginate.
show_disabled: :class:`bool`
Whether to show disabled buttons.
show_indicator: :class:`bool`
Expand All @@ -326,7 +373,7 @@ async def update(
"""

# Update pages and reset current_page to 0 (default)
self.pages: Union[List[PageGroup], List[str], List[Union[List[discord.Embed], discord.Embed]]] = (
self.pages: Union[List[PageGroup], List[str], List[Page], List[Union[List[discord.Embed], discord.Embed]]] = (
pages if pages is not None else self.pages
)
self.page_count = len(self.pages) - 1
Expand Down Expand Up @@ -361,7 +408,7 @@ async def on_timeout(self) -> None:
async def disable(
self,
include_custom: bool = False,
page: Optional[Union[str, Union[List[discord.Embed], discord.Embed]]] = None,
page: Optional[Union[str, Page, Union[List[discord.Embed], discord.Embed]]] = None,
) -> None:
"""Stops the paginator, disabling all of its components.
Expand All @@ -378,8 +425,8 @@ async def disable(
item.disabled = True
if page:
await self.message.edit(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page.content,
embeds=page.embeds,
view=self,
)
else:
Expand All @@ -388,7 +435,7 @@ async def disable(
async def cancel(
self,
include_custom: bool = False,
page: Optional[Union[str, Union[List[discord.Embed], discord.Embed]]] = None,
page: Optional[Union[str, Page, Union[List[discord.Embed], discord.Embed]]] = None,
) -> None:
"""Cancels the paginator, removing all of its components from the message.
Expand All @@ -406,8 +453,8 @@ async def cancel(
self.remove_item(item)
if page:
await self.message.edit(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page.content,
embeds=page.embeds,
view=self,
)
else:
Expand Down Expand Up @@ -439,8 +486,8 @@ async def goto_page(self, page_number=0) -> discord.Message:
page = self.get_page_content(page)

return await self.message.edit(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page.content,
embeds=page.embeds,
view=self,
)

Expand Down Expand Up @@ -588,17 +635,19 @@ def update_buttons(self) -> Dict:
return self.buttons

@staticmethod
def get_page_content(page: Union[str, discord.Embed, List[discord.Embed]]):
def get_page_content(page: Union[Page, str, discord.Embed, List[discord.Embed]]) -> Page:
"""Returns the correct content type for a page based on its content."""
if isinstance(page, discord.Embed):
return [page]
if isinstance(page, Page):
return page
elif isinstance(page, str):
return Page(content=page, embeds=[])
elif isinstance(page, discord.Embed):
return Page(content=None, embeds=[page])
elif isinstance(page, List):
if all(isinstance(x, discord.Embed) for x in page):
return page
return Page(content=None, embeds=page)
else:
raise TypeError("All list items must be embeds.")
elif isinstance(page, str):
return page

async def send(
self,
Expand Down Expand Up @@ -658,7 +707,7 @@ async def send(

self.update_buttons()
page = self.pages[self.current_page]
page = self.get_page_content(page)
page_content = self.get_page_content(page)

self.user = ctx.author

Expand All @@ -673,8 +722,8 @@ async def send(
ctx = target

self.message = await ctx.send(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page_content.content,
embeds=page_content.embeds,
view=self,
reference=reference,
allowed_mentions=allowed_mentions,
Expand Down Expand Up @@ -718,31 +767,31 @@ async def respond(

self.update_buttons()

page = self.pages[self.current_page]
page = self.get_page_content(page)
page: Union[Page, str, discord.Embed, List[discord.Embed]] = self.pages[self.current_page]
page_content: Page = self.get_page_content(page)

self.user = interaction.user
if target:
await interaction.response.send_message(target_message, ephemeral=ephemeral)
self.message = await target.send(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page_content.content,
embeds=page_content.embeds,
view=self,
)
else:
if interaction.response.is_done():
msg = await interaction.followup.send(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page_content.content,
embeds=page_content.embeds,
view=self,
ephemeral=ephemeral,
)
# convert from WebhookMessage to Message reference to bypass 15min webhook token timeout
msg = await msg.channel.fetch_message(msg.id)
else:
msg = await interaction.response.send_message(
content=page if isinstance(page, str) else None,
embeds=[] if isinstance(page, str) else page,
content=page_content.content,
embeds=page_content.embeds,
view=self,
ephemeral=ephemeral,
)
Expand Down
8 changes: 8 additions & 0 deletions docs/ext/pages/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,14 @@ Example usage in a cog:
API Reference
-------------

Page
~~~~

.. attributetable:: discord.ext.pages.Page

.. autoclass:: discord.ext.pages.Page
:members:

Paginator
~~~~~~~~~

Expand Down
23 changes: 23 additions & 0 deletions examples/views/paginator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ def __init__(self, bot):

self.even_more_pages = ["11111", "22222", "33333"]

self.new_pages = [
pages.Page(
content="Page 1 Title!",
embeds=[
discord.Embed(title="New Page 1 Embed Title 1!"),
discord.Embed(title="New Page 1 Embed Title 2!"),
],
),
pages.Page(
content="Page 2 Title!",
embeds=[
discord.Embed(title="New Page 2 Embed Title 1!"),
discord.Embed(title="New Page 2 Embed Title 2!"),
],
),
]

def get_pages(self):
return self.pages

Expand All @@ -48,6 +65,12 @@ async def pagetest_default(self, ctx: discord.ApplicationContext):
paginator = pages.Paginator(pages=self.get_pages())
await paginator.respond(ctx.interaction, ephemeral=False)

@pagetest.command(name="new")
async def pagetest_new(self, ctx: discord.ApplicationContext):
"""Demonstrates using the paginator with the Page class."""
paginator = pages.Paginator(pages=self.new_pages)
await paginator.respond(ctx.interaction, ephemeral=False)

@pagetest.command(name="hidden")
async def pagetest_hidden(self, ctx: discord.ApplicationContext):
"""Demonstrates using the paginator with disabled buttons hidden."""
Expand Down

0 comments on commit 24f05ed

Please sign in to comment.