From 7c3868ef3b93cb39dfea87cc6cbec4d103ccda3c Mon Sep 17 00:00:00 2001
From: Rapptz <rapptz@gmail.com>
Date: Fri, 21 Jul 2023 00:34:31 -0400
Subject: [PATCH] Add Client.remove_dynamic_items

---
 discord/client.py  | 28 ++++++++++++++++++++++++++--
 discord/state.py   |  3 +++
 discord/ui/view.py |  5 +++++
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/discord/client.py b/discord/client.py
index 51652b8a6..ff083c33b 100644
--- a/discord/client.py
+++ b/discord/client.py
@@ -2681,7 +2681,7 @@ class Client:
         return state.add_dm_channel(data)
 
     def add_dynamic_items(self, *items: Type[DynamicItem[Item[Any]]]) -> None:
-        r"""Registers a :class:`~discord.ui.DynamicItem` class for persistent listening.
+        r"""Registers :class:`~discord.ui.DynamicItem` classes for persistent listening.
 
         This method accepts *class types* rather than instances.
 
@@ -2695,7 +2695,7 @@ class Client:
         Raises
         -------
         TypeError
-            The class is not a subclass of :class:`~discord.ui.DynamicItem`.
+            A class is not a subclass of :class:`~discord.ui.DynamicItem`.
         """
 
         for item in items:
@@ -2704,6 +2704,30 @@ class Client:
 
         self._connection.store_dynamic_items(*items)
 
+    def remove_dynamic_items(self, *items: Type[DynamicItem[Item[Any]]]) -> None:
+        r"""Removes :class:`~discord.ui.DynamicItem` classes from persistent listening.
+
+        This method accepts *class types* rather than instances.
+
+        .. versionadded:: 2.4
+
+        Parameters
+        -----------
+        \*items: Type[:class:`~discord.ui.DynamicItem`]
+            The classes of dynamic items to remove.
+
+        Raises
+        -------
+        TypeError
+            A class is not a subclass of :class:`~discord.ui.DynamicItem`.
+        """
+
+        for item in items:
+            if not issubclass(item, DynamicItem):
+                raise TypeError(f'expected subclass of DynamicItem not {item.__name__}')
+
+        self._connection.remove_dynamic_items(*items)
+
     def add_view(self, view: View, *, message_id: Optional[int] = None) -> None:
         """Registers a :class:`~discord.ui.View` for persistent listening.
 
diff --git a/discord/state.py b/discord/state.py
index 18b115e8a..8dcc30c24 100644
--- a/discord/state.py
+++ b/discord/state.py
@@ -401,6 +401,9 @@ class ConnectionState(Generic[ClientT]):
     def store_dynamic_items(self, *items: Type[DynamicItem[Item[Any]]]) -> None:
         self._view_store.add_dynamic_items(*items)
 
+    def remove_dynamic_items(self, *items: Type[DynamicItem[Item[Any]]]) -> None:
+        self._view_store.remove_dynamic_items(*items)
+
     @property
     def persistent_views(self) -> Sequence[View]:
         return self._view_store.persistent_views
diff --git a/discord/ui/view.py b/discord/ui/view.py
index 9fc12b3ac..ea6104e20 100644
--- a/discord/ui/view.py
+++ b/discord/ui/view.py
@@ -557,6 +557,11 @@ class ViewStore:
             pattern = item.__discord_ui_compiled_template__
             self._dynamic_items[pattern] = item
 
+    def remove_dynamic_items(self, *items: Type[DynamicItem[Item[Any]]]) -> None:
+        for item in items:
+            pattern = item.__discord_ui_compiled_template__
+            self._dynamic_items.pop(pattern, None)
+
     def add_view(self, view: View, message_id: Optional[int] = None) -> None:
         view._start_listening_from_store(self)
         if view.__discord_ui_modal__: