From b9deb14b62dddbe97f4dab28edff1beaee2b958c Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 25 Mar 2026 17:43:06 -0500 Subject: [PATCH] feat: add Prev/Next navigation buttons to /refractor status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RefractorPaginationView with ◀ Prev / Next ▶ buttons - Buttons re-fetch from API on each page change - Prev disabled on page 1, Next disabled on last page - Only the command invoker can use the buttons - Buttons auto-disable after 2 min timeout - Single-page results show no buttons Co-Authored-By: Claude Opus 4.6 (1M context) --- cogs/refractor.py | 92 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/cogs/refractor.py b/cogs/refractor.py index a2e7284..28bd55a 100644 --- a/cogs/refractor.py +++ b/cogs/refractor.py @@ -129,6 +129,82 @@ def paginate(items: list, page: int, page_size: int = PAGE_SIZE) -> tuple: return items[start : start + page_size], total_pages +class RefractorPaginationView(discord.ui.View): + """Prev/Next buttons for refractor status pagination.""" + + def __init__( + self, + team: dict, + page: int, + total_pages: int, + total_count: int, + params: list, + owner_id: int, + timeout: float = 120.0, + ): + super().__init__(timeout=timeout) + self.team = team + self.page = page + self.total_pages = total_pages + self.total_count = total_count + self.base_params = params + self.owner_id = owner_id + self._update_buttons() + + def _update_buttons(self): + self.prev_btn.disabled = self.page <= 1 + self.next_btn.disabled = self.page >= self.total_pages + + async def _fetch_and_update(self, interaction: discord.Interaction): + offset = (self.page - 1) * PAGE_SIZE + params = [(k, v) for k, v in self.base_params if k != "offset"] + params.append(("offset", offset)) + + data = await db_get("refractor/cards", params=params) + items = data.get("items", []) if isinstance(data, dict) else [] + self.total_count = ( + data.get("count", self.total_count) + if isinstance(data, dict) + else self.total_count + ) + self.total_pages = max(1, (self.total_count + PAGE_SIZE - 1) // PAGE_SIZE) + self.page = min(self.page, self.total_pages) + + lines = [format_refractor_entry(state) for state in items] + embed = discord.Embed( + title=f"{self.team['sname']} Refractor Status", + description="\n\n".join(lines) if lines else "No cards found.", + color=0x6F42C1, + ) + embed.set_footer( + text=f"Page {self.page}/{self.total_pages} · {self.total_count} card(s) total" + ) + self._update_buttons() + await interaction.response.edit_message(embed=embed, view=self) + + @discord.ui.button(label="◀ Prev", style=discord.ButtonStyle.blurple) + async def prev_btn( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id != self.owner_id: + return + self.page = max(1, self.page - 1) + await self._fetch_and_update(interaction) + + @discord.ui.button(label="Next ▶", style=discord.ButtonStyle.blurple) + async def next_btn( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id != self.owner_id: + return + self.page = min(self.total_pages, self.page + 1) + await self._fetch_and_update(interaction) + + async def on_timeout(self): + self.prev_btn.disabled = True + self.next_btn.disabled = True + + class Refractor(commands.Cog): """Refractor progress tracking slash commands.""" @@ -237,8 +313,7 @@ class Refractor(commands.Cog): total_pages = max(1, (total_count + PAGE_SIZE - 1) // PAGE_SIZE) page = min(page, total_pages) - page_items = items - lines = [format_refractor_entry(state) for state in page_items] + lines = [format_refractor_entry(state) for state in items] embed = discord.Embed( title=f"{team['sname']} Refractor Status", @@ -249,7 +324,18 @@ class Refractor(commands.Cog): text=f"Page {page}/{total_pages} · {total_count} card(s) total" ) - await interaction.edit_original_response(embed=embed) + if total_pages > 1: + view = RefractorPaginationView( + team=team, + page=page, + total_pages=total_pages, + total_count=total_count, + params=params, + owner_id=interaction.user.id, + ) + await interaction.edit_original_response(embed=embed, view=view) + else: + await interaction.edit_original_response(embed=embed) async def setup(bot):