Skip to content

Commit d395b56

Browse files
committed
Dynamically update options in select menu based on current page.
1 parent 29c7cf0 commit d395b56

File tree

1 file changed

+90
-40
lines changed

1 file changed

+90
-40
lines changed

core/paginator.py

+90-40
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def __init__(self, ctx: commands.Context, *pages, **options):
5858
">": self.next_page,
5959
">>": self.last_page,
6060
}
61-
self._buttons_map = {"<<": None, "<": None, ">": None, ">>": None}
61+
self._buttons_map = {k: None for k in self.callback_map.keys()}
6262

6363
async def show_page(self, index: int) -> typing.Optional[typing.Dict]:
6464
"""
@@ -84,34 +84,19 @@ async def show_page(self, index: int) -> typing.Optional[typing.Dict]:
8484
self.update_disabled_status()
8585
return result
8686

87-
def update_disabled_status(self):
88-
if self.current == self.first_page():
89-
# disable << button
90-
if self._buttons_map["<<"] is not None:
91-
self._buttons_map["<<"].disabled = True
92-
93-
if self._buttons_map["<"] is not None:
94-
self._buttons_map["<"].disabled = True
95-
else:
96-
if self._buttons_map["<<"] is not None:
97-
self._buttons_map["<<"].disabled = False
98-
99-
if self._buttons_map["<"] is not None:
100-
self._buttons_map["<"].disabled = False
101-
102-
if self.current == self.last_page():
103-
# disable >> button
104-
if self._buttons_map[">>"] is not None:
105-
self._buttons_map[">>"].disabled = True
106-
107-
if self._buttons_map[">"] is not None:
108-
self._buttons_map[">"].disabled = True
109-
else:
110-
if self._buttons_map[">>"] is not None:
111-
self._buttons_map[">>"].disabled = False
112-
113-
if self._buttons_map[">"] is not None:
114-
self._buttons_map[">"].disabled = False
87+
def update_disabled_status(self) -> None:
88+
for k, v in self._buttons_map.items():
89+
if v is None:
90+
continue
91+
elif any(
92+
(
93+
self.current == self.first_page() and k in ("<<", "<"),
94+
self.current == self.last_page() and k in (">>", ">"),
95+
)
96+
):
97+
v.disabled = True
98+
else:
99+
v.disabled = False
115100

116101
async def create_base(self, item) -> None:
117102
"""
@@ -227,7 +212,7 @@ def __init__(self, handler: PaginatorSession, *args, **kwargs):
227212
async def stop_button(self, interaction: Interaction, button: Button):
228213
await self.handler.close(interaction=interaction)
229214

230-
def fill_items(self):
215+
def fill_items(self) -> None:
231216
if self.handler.select_menu is not None:
232217
self.add_item(self.handler.select_menu)
233218

@@ -246,7 +231,7 @@ def fill_items(self):
246231
self.add_item(button)
247232
self.add_item(self.stop_button)
248233

249-
async def interaction_check(self, interaction: Interaction):
234+
async def interaction_check(self, interaction: Interaction) -> bool:
250235
"""Only allow the message author to interact"""
251236
if interaction.user != self.handler.ctx.author:
252237
await interaction.response.send_message(
@@ -280,34 +265,96 @@ def __init__(self, handler, page_callback, **kwargs):
280265
self.handler = handler
281266
self.page_callback = page_callback
282267

283-
async def callback(self, interaction: Interaction):
268+
async def callback(self, interaction: Interaction) -> None:
284269
kwargs = await self.handler.show_page(self.page_callback())
270+
select_menu = self.handler.select_menu
271+
if select_menu is not None:
272+
select_menu.update_options(True)
285273
await interaction.response.edit_message(**kwargs, view=self.view)
286274

287275

288276
class PageSelect(Select):
289277
def __init__(self, handler: PaginatorSession, pages: typing.List[typing.Tuple[str]]):
290278
self.handler = handler
291-
options = []
279+
self.all_options = [] # no limit
292280
for n, (label, description) in enumerate(pages):
293-
options.append(discord.SelectOption(label=label, description=description, value=str(n)))
281+
self.all_options.append(discord.SelectOption(label=label, description=description, value=str(n)))
294282

295-
options = options[:25] # max 25 options
283+
options = self.update_options()
296284
super().__init__(placeholder="Select a page", min_values=1, max_values=1, options=options)
297285

298-
async def callback(self, interaction: Interaction):
286+
def update_options(self, refresh_options: bool = False) -> typing.List[discord.SelectOption]:
287+
"""
288+
A helper to dynamically update the select menu options based on the current page.
289+
"""
290+
current = self.handler.current
291+
curr_option = self.all_options[current]
292+
options = [curr_option]
293+
294+
add_previous = True
295+
add_next = True
296+
n = 0
297+
while len(options) < 25:
298+
if not add_previous and not add_next:
299+
break
300+
301+
pos = n + 1
302+
if current - pos < 0:
303+
add_previous = False
304+
if add_previous:
305+
option = self.all_options[current - pos]
306+
option.default = False
307+
options.insert(0, option)
308+
309+
if current + pos > len(self.all_options) - 1:
310+
add_next = False
311+
if add_next:
312+
option = self.all_options[current + pos]
313+
option.default = False
314+
options.append(option)
315+
316+
n = n + 1
317+
318+
if refresh_options:
319+
curr_option.default = True # pre-selected option
320+
self.options.clear()
321+
self.options = options
322+
323+
return options
324+
325+
async def callback(self, interaction: Interaction) -> None:
299326
page = int(self.values[0])
300327
kwargs = await self.handler.show_page(page)
328+
self.update_options(True)
301329
await interaction.response.edit_message(**kwargs, view=self.view)
302330

303331

304332
class EmbedPaginatorSession(PaginatorSession):
305-
def __init__(self, ctx: commands.Context, *embeds, **options):
333+
"""
334+
Class that interactively paginates embed pages.
335+
This inherits from PaginatorSession.
336+
337+
Parameters
338+
----------
339+
ctx : Context
340+
The context of the command.
341+
embeds : List[discord.Embed]
342+
A list of entries to paginate.
343+
create_select : bool
344+
Whether to create the select menu. Defaults to True.
345+
"""
346+
347+
def __init__(
348+
self,
349+
ctx: commands.Context,
350+
*embeds: typing.List[discord.Embed],
351+
create_select: bool = True,
352+
**options,
353+
):
306354
super().__init__(ctx, *embeds, **options)
307355

308356
if len(self.pages) > 1:
309357
select_options = []
310-
create_select = True
311358
for i, embed in enumerate(self.pages):
312359
footer_text = f"Page {i + 1} of {len(self.pages)}"
313360
if embed.footer.text:
@@ -320,6 +367,9 @@ def __init__(self, ctx: commands.Context, *embeds, **options):
320367
embed.set_footer(text=footer_text, icon_url=icon_url)
321368

322369
# select menu
370+
if not create_select:
371+
continue
372+
323373
if embed.author.name:
324374
title = embed.author.name[:30].strip()
325375
if len(embed.author.name) > 30:
@@ -355,7 +405,7 @@ def add_page(self, item: Embed) -> None:
355405
async def _create_base(self, item: Embed, view: View) -> None:
356406
self.base = await self.destination.send(embed=item, view=view)
357407

358-
def _show_page(self, page):
408+
def _show_page(self, page) -> typing.Dict:
359409
return dict(embed=page)
360410

361411

@@ -371,7 +421,7 @@ def add_page(self, item: str) -> None:
371421
else:
372422
raise TypeError("Page must be a str object.")
373423

374-
def _set_footer(self):
424+
def _set_footer(self) -> None:
375425
if self.embed is not None:
376426
footer_text = f"Page {self.current+1} of {len(self.pages)}"
377427
if self.footer_text:

0 commit comments

Comments
 (0)