[Feature] Add useful utility methods to ModalBuilder (#2773)
* Add method to get a component of type * Add methods to update a component * Add methods to remove a component(s) * Add missing `row` parameter to wrapper * Update XML documentation * Reorder properties to follow coding style * Remove unnecessary usings to follow coding style * Fix build usings * Fix XML documentation * Add `null` checks to remove methods * Fix empty constructor * Fix `CustomId` setter * Update src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs Co-authored-by: Misha133 <61027276+Misha-133@users.noreply.github.com> * Fix `GetComponent` to return null * Fix XML documentation * Add `null` checks * Update src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs * Update src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs * Update src/Discord.Net.Core/Entities/Interactions/Modals/ModalBuilder.cs Co-authored-by: Misha133 <61027276+Misha-133@users.noreply.github.com> --------- Co-authored-by: Misha133 <61027276+Misha-133@users.noreply.github.com>
This commit is contained in:
@@ -1,43 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a builder for creating a <see cref="Modal"/>.
|
||||||
|
/// </summary>
|
||||||
public class ModalBuilder
|
public class ModalBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the components of the current modal.
|
|
||||||
/// </summary>
|
|
||||||
public ModalComponentBuilder Components { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the title of the current modal.
|
|
||||||
/// </summary>
|
|
||||||
public string Title { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the custom id of the current modal.
|
|
||||||
/// </summary>
|
|
||||||
public string CustomId
|
|
||||||
{
|
|
||||||
get => _customId;
|
|
||||||
set => _customId = value?.Length switch
|
|
||||||
{
|
|
||||||
> ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom Id length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
|
|
||||||
0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom Id length must be at least 1."),
|
|
||||||
_ => value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _customId;
|
private string _customId;
|
||||||
|
|
||||||
public ModalBuilder() { }
|
public ModalBuilder() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of a <see cref="ModalBuilder"/>
|
/// Creates a new instance of the <see cref="ModalBuilder"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="title">The modal's title.</param>
|
/// <param name="title">The modal's title.</param>
|
||||||
/// <param name="customId">The modal's customId.</param>
|
/// <param name="customId">The modal's customId.</param>
|
||||||
@@ -50,6 +27,30 @@ namespace Discord
|
|||||||
Components = components ?? new();
|
Components = components ?? new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the title of the current modal.
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the custom ID of the current modal.
|
||||||
|
/// </summary>
|
||||||
|
public string CustomId
|
||||||
|
{
|
||||||
|
get => _customId;
|
||||||
|
set => _customId = value?.Length switch
|
||||||
|
{
|
||||||
|
> ComponentBuilder.MaxCustomIdLength => throw new ArgumentOutOfRangeException(nameof(value), $"Custom ID length must be less or equal to {ComponentBuilder.MaxCustomIdLength}."),
|
||||||
|
0 => throw new ArgumentOutOfRangeException(nameof(value), "Custom ID length must be at least 1."),
|
||||||
|
_ => value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the components of the current modal.
|
||||||
|
/// </summary>
|
||||||
|
public ModalComponentBuilder Components { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the title of the current modal.
|
/// Sets the title of the current modal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -76,10 +77,11 @@ namespace Discord
|
|||||||
/// Adds a component to the current builder.
|
/// Adds a component to the current builder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component">The component to add.</param>
|
/// <param name="component">The component to add.</param>
|
||||||
|
/// <param name="row">The row to add the text input.</param>
|
||||||
/// <returns>The current builder.</returns>
|
/// <returns>The current builder.</returns>
|
||||||
public ModalBuilder AddTextInput(TextInputBuilder component)
|
public ModalBuilder AddTextInput(TextInputBuilder component, int row = 0)
|
||||||
{
|
{
|
||||||
Components.WithTextInput(component);
|
Components.WithTextInput(component, row);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,21 +110,111 @@ namespace Discord
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <typeparamref name="TMessageComponent"/> by the specified <paramref name="customId"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TMessageComponent">The type of the component to get.</typeparam>
|
||||||
|
/// <param name="customId">The <see cref="IMessageComponent.CustomId"/> of the component to get.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The component of type <typeparamref name="TMessageComponent"/> that was found, <see langword="null"/> otherwise.
|
||||||
|
/// </returns>
|
||||||
|
public TMessageComponent GetComponent<TMessageComponent>(string customId)
|
||||||
|
where TMessageComponent : class, IMessageComponent
|
||||||
|
{
|
||||||
|
Preconditions.NotNull(customId, nameof(customId));
|
||||||
|
|
||||||
|
return Components.ActionRows
|
||||||
|
?.SelectMany(r => r.Components.OfType<TMessageComponent>())
|
||||||
|
.FirstOrDefault(c => c?.CustomId == customId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a <see cref="TextInputComponent"/> by the specified <paramref name="customId"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="customId">The <see cref="TextInputComponent.CustomId"/> of the input to update.</param>
|
||||||
|
/// <param name="updateTextInput">An action that configures the updated text input.</param>
|
||||||
|
/// <returns>The current builder.</returns>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when the <see cref="TextInputComponent"/> to be updated was not found.
|
||||||
|
/// </exception>
|
||||||
|
public ModalBuilder UpdateTextInput(string customId, Action<TextInputBuilder> updateTextInput)
|
||||||
|
{
|
||||||
|
Preconditions.NotNull(customId, nameof(customId));
|
||||||
|
|
||||||
|
var component = GetComponent<TextInputComponent>(customId) ?? throw new ArgumentException($"There is no component of type {nameof(TextInputComponent)} with the specified custom ID in this modal builder.", nameof(customId));
|
||||||
|
var row = Components.ActionRows.First(r => r.Components.Contains(component));
|
||||||
|
|
||||||
|
var builder = new TextInputBuilder
|
||||||
|
{
|
||||||
|
Label = component.Label,
|
||||||
|
CustomId = component.CustomId,
|
||||||
|
Style = component.Style,
|
||||||
|
Placeholder = component.Placeholder,
|
||||||
|
MinLength = component.MinLength,
|
||||||
|
MaxLength = component.MaxLength,
|
||||||
|
Required = component.Required,
|
||||||
|
Value = component.Value
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTextInput(builder);
|
||||||
|
|
||||||
|
row.Components.Remove(component);
|
||||||
|
row.AddComponent(builder.Build());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the value of a <see cref="TextInputComponent"/> by the specified <paramref name="customId"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="customId">The <see cref="TextInputComponent.CustomId"/> of the input to update.</param>
|
||||||
|
/// <param name="value">The new value to put.</param>
|
||||||
|
/// <returns>The current builder.</returns>
|
||||||
|
public ModalBuilder UpdateTextInput(string customId, object value)
|
||||||
|
{
|
||||||
|
UpdateTextInput(customId, x => x.Value = value?.ToString());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a component from this builder by the specified <paramref name="customId"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="customId">The <see cref="IMessageComponent.CustomId"/> of the component to remove.</param>
|
||||||
|
/// <returns>The current builder.</returns>
|
||||||
|
public ModalBuilder RemoveComponent(string customId)
|
||||||
|
{
|
||||||
|
Preconditions.NotNull(customId, nameof(customId));
|
||||||
|
|
||||||
|
Components.ActionRows?.ForEach(r => r.Components.RemoveAll(c => c.CustomId == customId));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all components of the given <paramref name="type"/> from this builder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The <see cref="ComponentType"/> to remove.</param>
|
||||||
|
/// <returns>The current builder.</returns>
|
||||||
|
public ModalBuilder RemoveComponentsOfType(ComponentType type)
|
||||||
|
{
|
||||||
|
Components.ActionRows?.ForEach(r => r.Components.RemoveAll(c => c.Type == type));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds this builder into a <see cref="Modal"/>.
|
/// Builds this builder into a <see cref="Modal"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <see cref="Modal"/> with the same values as this builder.</returns>
|
/// <returns>A <see cref="Modal"/> with the same values as this builder.</returns>
|
||||||
/// <exception cref="ArgumentException">Only TextInputComponents are allowed.</exception>
|
/// <exception cref="ArgumentException">Modals must have a custom ID.</exception>
|
||||||
/// <exception cref="ArgumentException">Modals must have a custom id.</exception>
|
|
||||||
/// <exception cref="ArgumentException">Modals must have a title.</exception>
|
/// <exception cref="ArgumentException">Modals must have a title.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Only components of type <see cref="TextInputComponent"/> are allowed.</exception>
|
||||||
public Modal Build()
|
public Modal Build()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(CustomId))
|
if (string.IsNullOrEmpty(CustomId))
|
||||||
throw new ArgumentException("Modals must have a custom id.", nameof(CustomId));
|
throw new ArgumentException("Modals must have a custom ID.", nameof(CustomId));
|
||||||
if (string.IsNullOrWhiteSpace(Title))
|
if (string.IsNullOrWhiteSpace(Title))
|
||||||
throw new ArgumentException("Modals must have a title.", nameof(Title));
|
throw new ArgumentException("Modals must have a title.", nameof(Title));
|
||||||
if (Components.ActionRows?.SelectMany(x => x.Components).Any(x => x.Type != ComponentType.TextInput) ?? false)
|
if (Components.ActionRows?.SelectMany(r => r.Components).Any(c => c.Type != ComponentType.TextInput) ?? false)
|
||||||
throw new ArgumentException($"Only TextInputComponents are allowed.", nameof(Components));
|
throw new ArgumentException($"Only components of type {nameof(TextInputComponent)} are allowed.", nameof(Components));
|
||||||
|
|
||||||
return new(Title, CustomId, Components.Build());
|
return new(Title, CustomId, Components.Build());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user