From 38ec5d1e691368f189eb54836723b6283691c6e0 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 6 Jun 2025 02:58:40 +0000 Subject: [PATCH 1/2] Add NullAccessory for non-interactive sections --- cogs/logging_cog.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cogs/logging_cog.py b/cogs/logging_cog.py index 9709160..ee9c7af 100644 --- a/cogs/logging_cog.py +++ b/cogs/logging_cog.py @@ -43,6 +43,15 @@ ALL_EVENT_KEYS = sorted([ # Add more audit keys if needed, e.g., "audit_stage_instance_create" ]) +class NullAccessory(ui.Button): + """Non-interactive accessory used as a placeholder.""" + + def __init__(self) -> None: + super().__init__(label="\u200b", disabled=True) + + def is_dispatchable(self) -> bool: # type: ignore[override] + return False + class LoggingCog(commands.Cog): """Handles comprehensive server event logging via webhooks with granular toggling.""" def __init__(self, bot: commands.Bot): @@ -75,7 +84,7 @@ class LoggingCog(commands.Cog): accessory=( ui.Thumbnail(media=author.display_avatar.url) if author - else None + else NullAccessory() ) ) self.header.add_item(ui.TextDisplay(f"**{title}**")) @@ -101,7 +110,7 @@ class LoggingCog(commands.Cog): def add_field(self, name: str, value: str, inline: bool = False): """Mimic Embed.add_field by appending a bolded name/value line.""" if not self._field_sections or len(self._field_sections[-1].children) >= 3: - section = ui.Section(accessory=None) + section = ui.Section(accessory=NullAccessory()) self._insert_field_section(section) self._field_sections.append(section) self._field_sections[-1].add_item(ui.TextDisplay(f"**{name}:** {value}")) From 0e70380d422c14ec1bd26c5cc3037329563ddee9 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 6 Jun 2025 03:27:16 +0000 Subject: [PATCH 2/2] Refactor LoggingCog view layout --- cogs/logging_cog.py | 94 +++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/cogs/logging_cog.py b/cogs/logging_cog.py index ee9c7af..fa760e7 100644 --- a/cogs/logging_cog.py +++ b/cogs/logging_cog.py @@ -43,14 +43,6 @@ ALL_EVENT_KEYS = sorted([ # Add more audit keys if needed, e.g., "audit_stage_instance_create" ]) -class NullAccessory(ui.Button): - """Non-interactive accessory used as a placeholder.""" - - def __init__(self) -> None: - super().__init__(label="\u200b", disabled=True) - - def is_dispatchable(self) -> bool: # type: ignore[override] - return False class LoggingCog(commands.Cog): """Handles comprehensive server event logging via webhooks with granular toggling.""" @@ -65,7 +57,7 @@ class LoggingCog(commands.Cog): asyncio.create_task(self.start_audit_log_poller_when_ready()) # Keep this for initial start class LogView(ui.LayoutView): - """Simple view for log messages with helper methods.""" + """View for logging messages using Discord's layout UI.""" def __init__( self, @@ -75,66 +67,76 @@ class LoggingCog(commands.Cog): color: discord.Color, author: Optional[discord.abc.User], footer: Optional[str], - ): + ) -> None: super().__init__(timeout=None) + self.container = ui.Container(accent_colour=color) self.add_item(self.container) - self.header = ui.Section( - accessory=( - ui.Thumbnail(media=author.display_avatar.url) - if author - else NullAccessory() - ) + self.description_display: Optional[ui.TextDisplay] = ( + ui.TextDisplay(description) if description else None ) - self.header.add_item(ui.TextDisplay(f"**{title}**")) - if description: - self.header.add_item(ui.TextDisplay(description)) - self.container.add_item(self.header) - # Placeholder for future field sections. They are inserted before - # the separator when the first field is added. - self._field_sections: list[ui.Section] = [] + # Header section is only used when an author is provided so we don't + # need a placeholder accessory. + if author is not None: + self.header: Optional[ui.Section] = ui.Section( + accessory=ui.Thumbnail(media=author.display_avatar.url) + ) + self.header.add_item(ui.TextDisplay(f"**{title}**")) + if self.description_display: + self.header.add_item(self.description_display) + self.container.add_item(self.header) + else: + self.header = None + self.title_display = ui.TextDisplay(f"**{title}**") + self.container.add_item(self.title_display) + if self.description_display: + self.container.add_item(self.description_display) + + # Container used for fields so they're inserted before the footer. + self.fields_container = ui.Container() + self.container.add_item(self.fields_container) self.separator = ui.Separator(spacing=discord.SeparatorSpacing.small) - footer_text = footer or f"Bot ID: {bot.user.id}" + ( f" | User ID: {author.id}" if author else "" ) self.footer_display = ui.TextDisplay(footer_text) - self.container.add_item(self.separator) self.container.add_item(self.footer_display) # --- Compatibility helpers --- def add_field(self, name: str, value: str, inline: bool = False): - """Mimic Embed.add_field by appending a bolded name/value line.""" - if not self._field_sections or len(self._field_sections[-1].children) >= 3: - section = ui.Section(accessory=NullAccessory()) - self._insert_field_section(section) - self._field_sections.append(section) - self._field_sections[-1].add_item(ui.TextDisplay(f"**{name}:** {value}")) - - def _insert_field_section(self, section: ui.Section) -> None: - """Insert a field section before the footer separator.""" - self.container.remove_item(self.separator) - self.container.remove_item(self.footer_display) - self.container.add_item(section) - self.container.add_item(self.separator) - self.container.add_item(self.footer_display) + """Append a bolded name/value line to the log view.""" + self.fields_container.add_item(ui.TextDisplay(f"**{name}:** {value}")) def set_footer(self, text: str): - """Mimic Embed.set_footer by replacing the footer text display.""" + """Replace the footer text display.""" self.footer_display.content = text def set_author(self, name: str, icon_url: Optional[str] = None): - """Mimic Embed.set_author by adjusting the header section.""" - self.header.clear_items() - if icon_url: - self.header.accessory = ui.Thumbnail(media=icon_url) + """Add or update the author information.""" + if self.header is None: + # Remove plain title/description displays and replace with a section. + self.container.remove_item(self.title_display) + if self.description_display: + self.container.remove_item(self.description_display) + self.header = ui.Section( + accessory=ui.Thumbnail(media=icon_url or "") + ) + self.header.add_item(ui.TextDisplay(name)) + if self.description_display: + self.header.add_item(self.description_display) + self.container.add_item(self.header) + # Move to the beginning to mimic embed header placement + self.container._children.remove(self.header) + self.container._children.insert(0, self.header) else: - self.header.accessory = None - self.header.add_item(ui.TextDisplay(name)) + self.header.clear_items() + if icon_url: + self.header.accessory = ui.Thumbnail(media=icon_url) + self.header.add_item(ui.TextDisplay(name)) def _user_display(self, user: Union[discord.Member, discord.User]) -> str: """Return display name, username and ID string for a user.""" display = user.display_name if isinstance(user, discord.Member) else user.name