From 0309aac3357a1f7250630aedc98e2b6b112528cc Mon Sep 17 00:00:00 2001 From: DA344 <108473820+DA-344@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:52:31 +0200 Subject: [PATCH] Add a swap method for dynamic item's dispatching --- discord/ui/action_row.py | 5 +++++ discord/ui/container.py | 5 +++++ discord/ui/item.py | 4 ++++ discord/ui/section.py | 5 +++++ discord/ui/view.py | 10 +++++++--- 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index 5b7dd4a27..a8da7e67d 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -67,6 +67,7 @@ if TYPE_CHECKING: from ..components import SelectOption from ..interactions import Interaction from .container import Container + from .dynamic import DynamicItem SelectCallbackDecorator = Callable[[ItemCallbackType['S', BaseSelectT]], BaseSelectT] @@ -194,6 +195,10 @@ class ActionRow(Item[V]): # it should error anyways. return True + def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None: + child_index = self._children.index(base) + self._children[child_index] = new # type: ignore + @property def width(self): return 5 diff --git a/discord/ui/container.py b/discord/ui/container.py index 1dcdca6b2..67a58a6b4 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -50,6 +50,7 @@ if TYPE_CHECKING: from ..components import Container as ContainerComponent from ..interactions import Interaction + from .dynamic import DynamicItem S = TypeVar('S', bound='Container', covariant=True) V = TypeVar('V', bound='LayoutView', covariant=True) @@ -198,6 +199,10 @@ class Container(Item[V]): def _has_children(self): return True + def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None: + child_index = self._children.index(base) + self._children[child_index] = new # type: ignore + @property def children(self) -> List[Item[V]]: """List[:class:`Item`]: The children of this container.""" diff --git a/discord/ui/item.py b/discord/ui/item.py index 9218d840d..3bcb4ad96 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -44,6 +44,7 @@ if TYPE_CHECKING: from ..components import Component from .action_row import ActionRow from .container import Container + from .dynamic import DynamicItem I = TypeVar('I', bound='Item[Any]') V = TypeVar('V', bound='BaseView', covariant=True) @@ -118,6 +119,9 @@ class Item(Generic[V]): return self._provided_custom_id return True + def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None: + raise ValueError + def __repr__(self) -> str: attrs = ' '.join(f'{key}={getattr(self, key)!r}' for key in self.__item_repr_attributes__) return f'<{self.__class__.__name__} {attrs}>' diff --git a/discord/ui/section.py b/discord/ui/section.py index a08f9e26b..847ac1e19 100644 --- a/discord/ui/section.py +++ b/discord/ui/section.py @@ -35,6 +35,7 @@ if TYPE_CHECKING: from typing_extensions import Self from .view import LayoutView + from .dynamic import DynamicItem from ..components import SectionComponent V = TypeVar('V', bound='LayoutView', covariant=True) @@ -117,6 +118,10 @@ class Section(Item[V]): def _is_v2(self) -> bool: return True + def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None: + if self.accessory.is_dispatchable() and getattr(self.accessory, 'custom_id', None) == custom_id: + self.accessory = new # type: ignore + def walk_children(self) -> Generator[Item[V], None, None]: """An iterator that recursively walks through all the children of this section and its children, if applicable. This includes the `accessory`. diff --git a/discord/ui/view.py b/discord/ui/view.py index 01f8543c6..c66fdf189 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -300,6 +300,12 @@ class BaseView: if self.__timeout: self.__timeout_expiry = time.monotonic() + self.__timeout + def _swap_item(self, base: Item, new: DynamicItem, custom_id: str) -> None: + # if an error is raised it is catched by the try/except block that calls + # this function + child_index = self._children.index(base) + self._children[child_index] = new # type: ignore + @property def timeout(self) -> Optional[float]: """Optional[:class:`float`]: The timeout in seconds from last interaction with the UI before no longer accepting input. @@ -954,11 +960,9 @@ class ViewStore: parent = base_item._parent or view try: - child_index = parent._children.index(base_item) # type: ignore + parent._swap_item(base_item, item, custom_id) except ValueError: return - else: - parent._children[child_index] = item # type: ignore item._view = view item._rendered_row = base_item._rendered_row