From be8e6e039405cae445c48e82719319c462e37b6d Mon Sep 17 00:00:00 2001
From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com>
Date: Fri, 9 May 2025 20:54:08 +0300
Subject: [PATCH] Component search fixes & improvements (#3123)
---
.../MessageComponents/ActionRowComponent.cs | 2 +-
.../Builders/ComponentContainerExtensions.cs | 98 +++++++++++++++++--
.../MessageComponents/ContainerComponent.cs | 2 +-
.../MessageComponents/INestedComponent.cs | 14 +++
.../MessageComponents/MessageComponent.cs | 2 +-
.../MessageComponents/SectionComponent.cs | 2 +-
6 files changed, 109 insertions(+), 11 deletions(-)
create mode 100644 src/Discord.Net.Core/Entities/Interactions/MessageComponents/INestedComponent.cs
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
index 6cdb1ffd..c604d535 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ActionRowComponent.cs
@@ -5,7 +5,7 @@ namespace Discord;
///
/// Represents a Row for child components to live in.
///
-public class ActionRowComponent : IMessageComponent
+public class ActionRowComponent : INestedComponent, IMessageComponent
{
///
public ComponentType Type => ComponentType.ActionRow;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
index 318367a5..9195a20b 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/Builders/ComponentContainerExtensions.cs
@@ -341,13 +341,30 @@ public static class ComponentContainerExtensions
///
public static ComponentT FindComponentById(this IComponentContainer container, int id)
where ComponentT : class, IMessageComponentBuilder
- => container.Components
- .OfType()
- .FirstOrDefault(x => x.Id == id)
- ?? container.Components
- .OfType()
- .Select(x => x.FindComponentById(id))
- .FirstOrDefault(x => x is not null);
+ {
+ if (container is ComponentT cmp && cmp.Id == id)
+ return cmp;
+
+ foreach (var component in container.Components)
+ {
+ if (component.Id == id && component is ComponentT target)
+ return target;
+
+ if (component is SectionBuilder section
+ && section.Accessory.Id == id
+ && section.Accessory is ComponentT targetAccessory)
+ return targetAccessory;
+
+ if (component is IComponentContainer childContainer)
+ {
+ var childSearchResult = childContainer.FindComponentById(id);
+ if (childSearchResult is not null)
+ return childSearchResult;
+ }
+ }
+
+ return null;
+ }
///
/// Gets a IEnumerable containing ids of
@@ -360,4 +377,71 @@ public static class ComponentContainerExtensions
.Concat(container.Components
.OfType()
.SelectMany(x => x.GetComponentIds()));
+
+ ///
+ /// Finds the first in the
+ /// or any of its child s with matching id.
+ ///
+ ///
+ /// The with matching id, otherwise.
+ ///
+ public static IMessageComponent FindComponentById(this INestedComponent container, int id)
+ => container.FindComponentById(id);
+
+ ///
+ /// Finds the first ComponentT in the
+ /// or any of its child s with matching id.
+ ///
+ ///
+ /// The ComponentT with matching id, otherwise.
+ ///
+ public static ComponentT FindComponentById(this INestedComponent container, int id)
+ where ComponentT : class, IMessageComponent
+ {
+ if (container is ComponentT cmp && cmp.Id == id)
+ return cmp;
+
+ return container.Components.FindComponentById(id);
+ }
+
+ ///
+ /// Finds the first ComponentT in the
+ /// or any of its child s with matching id.
+ ///
+ ///
+ /// The ComponentT with matching id, otherwise.
+ ///
+ public static ComponentT FindComponentById(this IEnumerable components, int id)
+ where ComponentT : class, IMessageComponent
+ {
+ foreach (var component in components)
+ {
+ if (component.Id == id && component is ComponentT target)
+ return target;
+
+ if (component is SectionComponent section
+ && section.Accessory.Id == id
+ && section.Accessory is ComponentT targetAccessory)
+ return targetAccessory;
+
+ if (component is INestedComponent childContainer)
+ {
+ var childSearchResult = childContainer.FindComponentById(id);
+ if (childSearchResult is not null)
+ return childSearchResult;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Finds the first in the
+ /// or any of its child s with matching id.
+ ///
+ ///
+ /// The with matching id, otherwise.
+ ///
+ public static IMessageComponent FindComponentById(this IEnumerable components, int id)
+ => components.FindComponentById(id);
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
index 9e8fe1fa..c01417f7 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/ContainerComponent.cs
@@ -5,7 +5,7 @@ namespace Discord;
///
/// Represents a container component.
///
-public class ContainerComponent : IMessageComponent
+public class ContainerComponent : INestedComponent, IMessageComponent
{
///
public ComponentType Type => ComponentType.Container;
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/INestedComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/INestedComponent.cs
new file mode 100644
index 00000000..68190fbb
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/INestedComponent.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+
+namespace Discord;
+
+///
+/// Represents a containing child components.
+///
+public interface INestedComponent
+{
+ ///
+ /// Gets the child components in this container.
+ ///
+ IReadOnlyCollection Components { get; }
+}
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs
index 18946a73..cf2628f2 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/MessageComponent.cs
@@ -5,7 +5,7 @@ namespace Discord;
///
/// Represents a component object used to send components with messages.
///
-public class MessageComponent
+public class MessageComponent : INestedComponent
{
///
/// Gets the components to be used in a message.
diff --git a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
index 30ddcb1a..6d88609d 100644
--- a/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/MessageComponents/SectionComponent.cs
@@ -5,7 +5,7 @@ namespace Discord;
///
/// Represents a section component.
///
-public class SectionComponent : IMessageComponent
+public class SectionComponent : INestedComponent, IMessageComponent
{
///
public ComponentType Type => ComponentType.Section;