Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Discord;
/// </summary>
public class ButtonBuilder : IInteractableComponentBuilder
{
/// <inheritdoc />
public ComponentType Type => ComponentType.Button;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,55 @@ public static BuilderT WithActionRow<BuilderT>(this BuilderT container,
return container.WithActionRow(cont);
}

/// <summary>
/// Adds a <see cref="FileUploadComponentBuilder"/> to the container.
/// </summary>
/// <returns>
/// The current container.
/// </returns>
public static BuilderT WithFileUpload<BuilderT>(this BuilderT container, FileUploadComponentBuilder separator)
where BuilderT : class, IInteractableComponentContainer
{
container.AddComponent(separator);
return container;
}

/// <summary>
/// Adds a <see cref="FileUploadComponentBuilder"/> to the container.
/// </summary>
/// <returns>
/// The current container.
/// </returns>
public static BuilderT WithFileUpload<BuilderT>(this BuilderT container,
string customId,
int? minValues = null,
int? maxValues = null,
bool isRequired = false,
int? id = null)
where BuilderT : class, IInteractableComponentContainer
=> container.WithFileUpload(new FileUploadComponentBuilder()
.WithCustomId(customId)
.WithMinValues(minValues)
.WithMaxValues(maxValues)
.WithRequired(isRequired)
.WithId(id));

/// <summary>
/// Adds a <see cref="FileUploadComponentBuilder"/> to the container.
/// </summary>
/// <returns>
/// The current container.
/// </returns>
public static BuilderT WithFileUpload<BuilderT>(this BuilderT container,
Action<FileUploadComponentBuilder> options)
where BuilderT : class, IInteractableComponentContainer
{
var comp = new FileUploadComponentBuilder();
options(comp);
return container.WithFileUpload(comp);
}


/// <summary>
/// Finds the first <see cref="IMessageComponentBuilder"/> in the <see cref="IComponentContainer"/>
/// or any of its child <see cref="IComponentContainer"/>s with matching id.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
namespace Discord;

public class FileUploadComponentBuilder : IInteractableComponentBuilder
{
public const int MaxFileCount = 10;

/// <inheritdoc/>
public ComponentType Type => ComponentType.FileUpload;

/// <summary>
///
/// </summary>
public string CustomId { get; set; }

/// <summary>
///
/// </summary>
public int? Id { get; set; }

/// <summary>
///
/// </summary>
public int? MinValues { get; set; }

/// <summary>
///
/// </summary>
public int? MaxValues { get; set; }

/// <summary>
///
/// </summary>
public bool IsRequired { get; set; } = false;

/// <summary>
///
/// </summary>
public FileUploadComponentBuilder WithCustomId(string customId)
{
CustomId = customId;
return this;
}

/// <summary>
///
/// </summary>
public FileUploadComponentBuilder WithMinValues(int? minValues)
{
MinValues = minValues;
return this;
}

/// <summary>
///
/// </summary>
public FileUploadComponentBuilder WithMaxValues(int? maxValues)
{
MaxValues = maxValues;
return this;
}

/// <summary>
///
/// </summary>
public FileUploadComponentBuilder WithRequired(bool isRequired)
{
IsRequired = isRequired;
return this;
}

/// <summary>
///
/// </summary>
public FileUploadComponentBuilder() {}

/// <summary>
///
/// </summary>
public FileUploadComponentBuilder(string customId, int? minValues = null, int? maxValues = null, bool isRequired = false, int? id = null)
{
CustomId = customId;
MinValues = minValues;
MaxValues = maxValues;
IsRequired = isRequired;
Id = id;
}

public FileUploadComponent Build()
{
Preconditions.NotNullOrWhitespace(CustomId, nameof(CustomId));

if (MinValues is not null && MaxValues is not null)
Preconditions.AtLeast(MaxValues.Value, MinValues.Value, nameof(MaxValues));

Preconditions.AtMost(MinValues ?? 0, MaxFileCount, nameof(MinValues));
Preconditions.AtMost(MaxValues ?? 0, MaxFileCount, nameof(MaxValues));

return new FileUploadComponent(Id, CustomId, MinValues, MaxValues, IsRequired);
}

/// <inheritdoc/>
IMessageComponent IMessageComponentBuilder.Build() => Build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Collections.Immutable;
using System.Linq;

namespace Discord;

public class LabelBuilder : IMessageComponentBuilder
{
/// <inheritdoc cref="IComponentContainer.SupportedComponentTypes"/>
public ImmutableArray<ComponentType> SupportedComponentTypes { get; } =
[
ComponentType.SelectMenu,
ComponentType.TextInput,
ComponentType.UserSelect,
ComponentType.RoleSelect,
ComponentType.MentionableSelect,
ComponentType.ChannelSelect,
ComponentType.FileUpload
];

/// <summary>
/// The maximum length of the label.
/// </summary>
public const int MaxLabelLength = 100;

/// <summary>
/// The maximum length of the description.
/// </summary>
public const int MaxDescriptionLength = 69420; // TODO: set to the real limit

/// <inheritdoc />
public ComponentType Type => ComponentType.Label;

/// <inheritdoc />
public int? Id { get; set; }

/// <summary>
///
/// </summary>
public string Label { get; set; }

/// <summary>
///
/// </summary>
public string Description { get; set; }

public IMessageComponentBuilder Component { get; set; }

/// <summary>
/// Initializes a new <see cref="LabelBuilder"/>.
/// </summary>
public LabelBuilder() { }

/// <summary>
/// Initializes a new <see cref="LabelBuilder"/> with the specified content.
/// </summary>
public LabelBuilder(string label, IMessageComponentBuilder component, string description = null, int? id = null)
{
Id = id;
Label = label;
Component = component;
Description = description;
}

/// <summary>
/// Initializes a new <see cref="LabelBuilder"/> from existing component.
/// </summary>
public LabelBuilder(LabelComponent label)
{
Label = label.Label;
Description = label.Description;
Id = label.Id;
Component = label.Component.ToBuilder();
}

public LabelComponent Build()
{
Preconditions.NotNullOrWhitespace(Label, nameof(Label));
Preconditions.AtMost(Label.Length, MaxLabelLength, nameof(Label));

Preconditions.AtMost(Description?.Length ?? 0, MaxDescriptionLength, nameof(Description));

Preconditions.NotNull(Component, nameof(Component));

if (SupportedComponentTypes.All(x => Component.Type != x))
throw new InvalidOperationException($"Component can only be {nameof(SelectMenuBuilder)} or {nameof(TextInputBuilder)}.");

return new LabelComponent(Id, Label, Description, Component.Build());
}

/// <inheritdoc />
IMessageComponent IMessageComponentBuilder.Build() => Build();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class MediaGalleryBuilder : IMessageComponentBuilder
/// <inheritdoc/>
public int? Id { get; set; }

private List<MediaGalleryItemProperties> _items = new();
private List<MediaGalleryItemProperties> _items = [];

/// <summary>
/// Initializes a new instance of the <see cref="MediaGalleryBuilder"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,49 @@ public enum ComponentType
/// </summary>
ChannelSelect = 8,

/// <summary>
/// A container to display text alongside an accessory component.
/// </summary>
Section = 9,

/// <summary>
/// A component displaying Markdown text.
/// </summary>
TextDisplay = 10,

/// <summary>
/// A small image that can be used as an accessory.
/// </summary>
Thumbnail = 11,

/// <summary>
/// A component displaying images and other media.
/// </summary>
MediaGallery = 12,

/// <summary>
/// A component displaying an attached file.
/// </summary>
File = 13,

/// <summary>
/// A component to add vertical padding between other components.
/// </summary>
Separator = 14,

/// <summary>
/// A container that visually groups a set of components.
/// </summary>
Container = 17,

/// <summary>
///
/// </summary>
Label = 18,

/// <summary>
///
/// </summary>
FileUpload = 19,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Discord;

public class FileUploadComponent : IMessageComponent
{
/// <inheritdoc/>
public ComponentType Type => ComponentType.FileUpload;
/// <summary>
/// Gets the ID of this component.
/// </summary>
public int? Id { get; }

/// <summary>
/// Gets the custom ID of this component.
/// </summary>
public string CustomId { get; }

/// <summary>
/// Gets the minimum number of files a user must upload.
/// </summary>
public int? MinValues { get; }

/// <summary>
/// Gets the maximum number of files a user can upload.
/// </summary>
public int? MaxValues { get; }

/// <summary>
/// Gets whether this component requires a file upload to be submitted.
/// </summary>
public bool IsRequired { get; }

internal FileUploadComponent(int? id, string customId, int? minValues, int? maxValues, bool isRequired)
{
Id = id;
CustomId = customId;
MinValues = minValues;
MaxValues = maxValues;
IsRequired = isRequired;
}

/// <inheritdoc cref="IMessageComponent.ToBuilder"/>
public FileUploadComponentBuilder ToBuilder()
=> new(CustomId, MinValues, MaxValues, IsRequired, Id);

/// <inheritdoc/>
IMessageComponentBuilder IMessageComponent.ToBuilder() => ToBuilder();
}
Loading