diff --git a/src/Disqord.Core/Entities/Core/Interactions/Components/IDefaultSelectionValue.cs b/src/Disqord.Core/Entities/Core/Interactions/Components/IDefaultSelectionValue.cs new file mode 100644 index 000000000..8e3d85239 --- /dev/null +++ b/src/Disqord.Core/Entities/Core/Interactions/Components/IDefaultSelectionValue.cs @@ -0,0 +1,12 @@ +namespace Disqord; + +/// +/// Represents a default value of a selection message component. +/// +public interface IDefaultSelectionValue: IIdentifiableEntity +{ + /// + /// Gets the type of this default value. + /// + DefaultSelectionValueType Type { get; } +} diff --git a/src/Disqord.Core/Entities/Core/Interactions/Components/ISelectionComponent.cs b/src/Disqord.Core/Entities/Core/Interactions/Components/ISelectionComponent.cs index c87f336b6..673d233c1 100644 --- a/src/Disqord.Core/Entities/Core/Interactions/Components/ISelectionComponent.cs +++ b/src/Disqord.Core/Entities/Core/Interactions/Components/ISelectionComponent.cs @@ -26,6 +26,15 @@ public interface ISelectionComponent : IComponent, ICustomIdentifiableEntity /// string? Placeholder { get; } + /// + /// Gets the default values that auto-populate this selection component. + /// + /// + /// This is only valid for , + /// , or selections. + /// + IReadOnlyList DefaultValues { get; } + /// /// Gets the minimum amount of options that must be selected at once of this selection component. /// diff --git a/src/Disqord.Core/Entities/Local/Interaction/Components/LocalComponent.cs b/src/Disqord.Core/Entities/Local/Interaction/Components/LocalComponent.cs index 0638b179d..2781b97cf 100644 --- a/src/Disqord.Core/Entities/Local/Interaction/Components/LocalComponent.cs +++ b/src/Disqord.Core/Entities/Local/Interaction/Components/LocalComponent.cs @@ -113,6 +113,7 @@ public virtual ComponentJsonModel ToModel() model.Type = (ComponentType) selectionComponent.Type; model.ChannelTypes = Optional.Convert(selectionComponent.ChannelTypes, channelTypes => channelTypes?.ToArray())!; model.Placeholder = selectionComponent.Placeholder; + model.DefaultValues = Optional.Convert(selectionComponent.DefaultValues, defaultValues => defaultValues.Select(defaultValue => defaultValue.ToModel()).ToArray()); model.MinValues = selectionComponent.MinimumSelectedOptions; model.MaxValues = selectionComponent.MaximumSelectedOptions; model.Disabled = selectionComponent.IsDisabled; diff --git a/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/Extensions/LocalSelectionComponentExtensions.cs b/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/Extensions/LocalSelectionComponentExtensions.cs index 3f9504b15..043a4c5e7 100644 --- a/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/Extensions/LocalSelectionComponentExtensions.cs +++ b/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/Extensions/LocalSelectionComponentExtensions.cs @@ -49,6 +49,32 @@ public static TComponent WithPlaceholder(this TComponent selection, return selection; } + public static TComponent AddDefaultValue(this TComponent selection, LocalDefaultSelectionValue defaultValue) + where TComponent : LocalSelectionComponent + { + if (selection.DefaultValues.Add(defaultValue, out var list)) + selection.DefaultValues = new(list); + + return selection; + } + + public static TComponent WithDefaultValues(this TComponent selection, IEnumerable defaultValues) + where TComponent : LocalSelectionComponent + { + Guard.IsNotNull(defaultValues); + + if (selection.DefaultValues.With(defaultValues, out var list)) + selection.DefaultValues = new(list); + + return selection; + } + + public static TComponent WithDefaultValues(this TComponent selection, params LocalDefaultSelectionValue[] defaultValues) + where TComponent : LocalSelectionComponent + { + return selection.WithDefaultValues(defaultValues as IEnumerable); + } + public static TComponent WithMinimumSelectedOptions(this TComponent selection, int minimumSelectedOptions) where TComponent : LocalSelectionComponent { diff --git a/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalDefaultSelectionValue.cs b/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalDefaultSelectionValue.cs new file mode 100644 index 000000000..75433a0ba --- /dev/null +++ b/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalDefaultSelectionValue.cs @@ -0,0 +1,69 @@ +using Disqord.Models; + +namespace Disqord; + +public class LocalDefaultSelectionValue : IDefaultSelectionValue, ILocalConstruct, IJsonConvertible +{ + public static LocalDefaultSelectionValue User(Snowflake id) + { + return new(id, DefaultSelectionValueType.User); + } + + public static LocalDefaultSelectionValue Role(Snowflake id) + { + return new(id, DefaultSelectionValueType.Role); + } + + public static LocalDefaultSelectionValue Channel(Snowflake id) + { + return new(id, DefaultSelectionValueType.Channel); + } + + /// + public Snowflake Id { get; set; } + + /// + public DefaultSelectionValueType Type { get; set; } + + /// + /// Instantiates a new . + /// + public LocalDefaultSelectionValue() + { } + + /// + /// Instantiates a new . + /// + /// The id of the entity. + /// The type of the entity. + public LocalDefaultSelectionValue(Snowflake id, DefaultSelectionValueType type) + { + Id = id; + Type = type; + } + + /// + /// Instantiates a new with the properties copied from another instance. + /// + /// The other instance to copy properties from. + protected LocalDefaultSelectionValue(LocalDefaultSelectionValue other) + { + Id = other.Id; + Type = other.Type; + } + + /// + public LocalDefaultSelectionValue Clone() + { + return new(this); + } + + public DefaultValueJsonModel ToModel() + { + return new DefaultValueJsonModel + { + Id = Id, + Type = Type + }; + } +} diff --git a/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalSelectionComponent.cs b/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalSelectionComponent.cs index cc3cb9df5..e41ffa8b9 100644 --- a/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalSelectionComponent.cs +++ b/src/Disqord.Core/Entities/Local/Interaction/Components/Selection/LocalSelectionComponent.cs @@ -30,6 +30,15 @@ public class LocalSelectionComponent : LocalComponent, ILocalCustomIdentifiableE /// public Optional Placeholder { get; set; } + /// + /// Gets or sets the default values of this selection. + /// + /// + /// This is only valid for , + /// , or selections. + /// + public Optional> DefaultValues { get; set; } + /// /// Gets or sets the minimum amount of options of this selection. /// @@ -69,6 +78,7 @@ protected LocalSelectionComponent(LocalSelectionComponent other) Type = other.Type; ChannelTypes = other.ChannelTypes.Clone(); Placeholder = other.Placeholder; + DefaultValues = other.DefaultValues.DeepClone(); MinimumSelectedOptions = other.MinimumSelectedOptions; MaximumSelectedOptions = other.MaximumSelectedOptions; Options = other.Options.DeepClone(); @@ -112,6 +122,19 @@ public static LocalSelectionComponent CreateFrom(ISelectionComponent selectionCo selection.Options = localOptions; } + else + { + var defaultValues = selectionComponent.DefaultValues; + var defaultValueCount = defaultValues.Count; + var localDefaultValues = new List(defaultValueCount); + for (var i = 0; i < defaultValueCount; i++) + { + var defaultValue = defaultValues[i]; + localDefaultValues.Add(new(defaultValue.Id, defaultValue.Type)); + } + + selection.DefaultValues = localDefaultValues; + } return selection; } diff --git a/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientDefaultSelectionValue.cs b/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientDefaultSelectionValue.cs new file mode 100644 index 000000000..9fc70b50f --- /dev/null +++ b/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientDefaultSelectionValue.cs @@ -0,0 +1,17 @@ +using Disqord.Models; + +namespace Disqord; + +/// +public class TransientDefaultSelectionValue : TransientClientEntity, IDefaultSelectionValue +{ + /// + public Snowflake Id => Model.Id; + + /// + public DefaultSelectionValueType Type => Model.Type; + + public TransientDefaultSelectionValue(IClient client, DefaultValueJsonModel model) + : base(client, model) + { } +} diff --git a/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientSelectionComponent.cs b/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientSelectionComponent.cs index 9eab826e6..666147962 100644 --- a/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientSelectionComponent.cs +++ b/src/Disqord.Core/Entities/Transient/Interactions/Components/TransientSelectionComponent.cs @@ -29,6 +29,11 @@ public IReadOnlyList ChannelTypes /// public string? Placeholder => Model.Placeholder.GetValueOrDefault(); + /// + public IReadOnlyList DefaultValues => _defaultValues ??= Model.DefaultValues.Value.ToReadOnlyList(Client, (model, client) => new TransientDefaultSelectionValue(client, model)); + + private IReadOnlyList? _defaultValues; + /// public int MinimumSelectedOptions => Model.MinValues.Value; diff --git a/src/Disqord.Core/Enums/Interactions/Components/DefaultSelectionValueType.cs b/src/Disqord.Core/Enums/Interactions/Components/DefaultSelectionValueType.cs new file mode 100644 index 000000000..8d550c8c1 --- /dev/null +++ b/src/Disqord.Core/Enums/Interactions/Components/DefaultSelectionValueType.cs @@ -0,0 +1,20 @@ +using System.Runtime.Serialization; +using Disqord.Serialization.Json; + +namespace Disqord; + +/// +/// Represents the type of default selection value. +/// +[StringEnum] +public enum DefaultSelectionValueType +{ + [EnumMember(Value = "user")] + User, + + [EnumMember(Value = "role")] + Role, + + [EnumMember(Value = "channel")] + Channel, +} diff --git a/src/Disqord.Core/Models/Interactions/Components/ComponentJsonModel.cs b/src/Disqord.Core/Models/Interactions/Components/ComponentJsonModel.cs index 982e0d836..34d7c3529 100644 --- a/src/Disqord.Core/Models/Interactions/Components/ComponentJsonModel.cs +++ b/src/Disqord.Core/Models/Interactions/Components/ComponentJsonModel.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Disqord.Serialization.Json; using Qommon; @@ -39,6 +40,9 @@ public class ComponentJsonModel : JsonModel [JsonProperty("placeholder")] public Optional Placeholder; + [JsonProperty("default_values")] + public Optional DefaultValues; + [JsonProperty("min_values")] public Optional MinValues; @@ -137,6 +141,22 @@ protected override void OnValidate() if (MinValues.HasValue && MaxValues.HasValue) Guard.IsLessThanOrEqualTo(MinValues.Value, MaxValues.Value); + OptionalGuard.CheckValue(DefaultValues, defaultValues => + { + Guard.IsBetweenOrEqualTo(defaultValues.Length, MinValues.GetValueOrDefault(Discord.Limits.Component.Selection.MinMinimumSelectedOptions), MaxValues.GetValueOrDefault(Discord.Limits.Component.Selection.MaxMaximumSelectedOptions)); + + Predicate predicate = Type switch + { + ComponentType.UserSelection => value => value.Type is DefaultSelectionValueType.User, + ComponentType.RoleSelection => value => value.Type is DefaultSelectionValueType.Role, + ComponentType.MentionableSelection => value => value.Type is DefaultSelectionValueType.User or DefaultSelectionValueType.Role, + ComponentType.ChannelSelection => value => value.Type is DefaultSelectionValueType.Channel, + _ => value => true + }; + + Guard.IsTrue(Array.TrueForAll(defaultValues, predicate), message: "The types of default selection values must match the type of the component."); + }); + break; } case ComponentType.TextInput: diff --git a/src/Disqord.Core/Models/Interactions/Components/DefaultValueJsonModel.cs b/src/Disqord.Core/Models/Interactions/Components/DefaultValueJsonModel.cs new file mode 100644 index 000000000..3414d74a1 --- /dev/null +++ b/src/Disqord.Core/Models/Interactions/Components/DefaultValueJsonModel.cs @@ -0,0 +1,12 @@ +using Disqord.Serialization.Json; + +namespace Disqord.Models; + +public class DefaultValueJsonModel : JsonModel +{ + [JsonProperty("id")] + public Snowflake Id; + + [JsonProperty("type")] + public DefaultSelectionValueType Type; +}