| @@ -9,14 +9,12 @@ namespace Discord | |||||
| { | { | ||||
| public class ActionRowComponent : IMessageComponent | public class ActionRowComponent : IMessageComponent | ||||
| { | { | ||||
| [JsonProperty("type")] | |||||
| public ComponentType Type { get; } = ComponentType.ActionRow; | public ComponentType Type { get; } = ComponentType.ActionRow; | ||||
| [JsonProperty("components")] | |||||
| public IReadOnlyCollection<IMessageComponent> Components { get; internal set; } | |||||
| public IReadOnlyCollection<ButtonComponent> Components { get; internal set; } | |||||
| internal ActionRowComponent() { } | internal ActionRowComponent() { } | ||||
| internal ActionRowComponent(IReadOnlyCollection<IMessageComponent> components) | |||||
| internal ActionRowComponent(List<ButtonComponent> components) | |||||
| { | { | ||||
| this.Components = components; | this.Components = components; | ||||
| } | } | ||||
| @@ -9,25 +9,18 @@ namespace Discord | |||||
| { | { | ||||
| public class ButtonComponent : IMessageComponent | public class ButtonComponent : IMessageComponent | ||||
| { | { | ||||
| [JsonProperty("type")] | |||||
| public ComponentType Type { get; } = ComponentType.Button; | public ComponentType Type { get; } = ComponentType.Button; | ||||
| [JsonProperty("style")] | |||||
| public ButtonStyle Style { get; } | public ButtonStyle Style { get; } | ||||
| [JsonProperty("label")] | |||||
| public string Label { get; } | public string Label { get; } | ||||
| [JsonProperty("emoji")] | |||||
| public IEmote Emote { get; } | public IEmote Emote { get; } | ||||
| [JsonProperty("custom_id")] | |||||
| public string CustomId { get; } | public string CustomId { get; } | ||||
| [JsonProperty("url")] | |||||
| public string Url { get; } | public string Url { get; } | ||||
| [JsonProperty("disabled")] | |||||
| public bool Disabled { get; } | public bool Disabled { get; } | ||||
| internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool disabled) | internal ButtonComponent(ButtonStyle style, string label, IEmote emote, string customId, string url, bool disabled) | ||||
| @@ -39,5 +32,7 @@ namespace Discord | |||||
| this.Url = url; | this.Url = url; | ||||
| this.Disabled = disabled; | this.Disabled = disabled; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -87,7 +87,7 @@ namespace Discord | |||||
| public class ActionRowBuilder | public class ActionRowBuilder | ||||
| { | { | ||||
| public const int MaxChildCount = 5; | public const int MaxChildCount = 5; | ||||
| public List<IMessageComponent> Components | |||||
| public List<ButtonComponent> Components | |||||
| { | { | ||||
| get => _components; | get => _components; | ||||
| set | set | ||||
| @@ -99,18 +99,18 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| private List<IMessageComponent> _components { get; set; } | |||||
| private List<ButtonComponent> _components { get; set; } | |||||
| public ActionRowBuilder WithComponents(List<IMessageComponent> components) | |||||
| public ActionRowBuilder WithComponents(List<ButtonComponent> components) | |||||
| { | { | ||||
| this.Components = components; | this.Components = components; | ||||
| return this; | return this; | ||||
| } | } | ||||
| public ActionRowBuilder WithComponent(IMessageComponent component) | |||||
| public ActionRowBuilder WithComponent(ButtonComponent component) | |||||
| { | { | ||||
| if (this.Components == null) | if (this.Components == null) | ||||
| this.Components = new List<IMessageComponent>(); | |||||
| this.Components = new List<ButtonComponent>(); | |||||
| this.Components.Add(component); | this.Components.Add(component); | ||||
| @@ -118,7 +118,15 @@ namespace Discord | |||||
| } | } | ||||
| public ActionRowComponent Build() | public ActionRowComponent Build() | ||||
| => new ActionRowComponent(this._components); | |||||
| { | |||||
| if (this.Components == null) | |||||
| throw new ArgumentNullException($"{nameof(Components)} cannot be null!"); | |||||
| if (this.Components.Count == 0) | |||||
| throw new ArgumentException("There must be at least 1 component in a row"); | |||||
| return new ActionRowComponent(this._components); | |||||
| } | |||||
| } | } | ||||
| public class ButtonBuilder | public class ButtonBuilder | ||||
| @@ -8,7 +8,7 @@ namespace Discord | |||||
| { | { | ||||
| public class MessageComponent | public class MessageComponent | ||||
| { | { | ||||
| public IReadOnlyCollection<IMessageComponent> Components { get; } | |||||
| public IReadOnlyCollection<ActionRowComponent> Components { get; } | |||||
| internal MessageComponent(List<ActionRowComponent> components) | internal MessageComponent(List<ActionRowComponent> components) | ||||
| { | { | ||||
| @@ -17,8 +17,5 @@ namespace Discord | |||||
| internal static MessageComponent Empty | internal static MessageComponent Empty | ||||
| => new MessageComponent(new List<ActionRowComponent>()); | => new MessageComponent(new List<ActionRowComponent>()); | ||||
| internal IMessageComponent[] ToModel() | |||||
| => this.Components.ToArray(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,25 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class ActionRowComponent | |||||
| { | |||||
| [JsonProperty("type")] | |||||
| public ComponentType Type { get; set; } | |||||
| [JsonProperty("components")] | |||||
| public List<ButtonComponent> Components { get; set; } | |||||
| internal ActionRowComponent() { } | |||||
| internal ActionRowComponent(Discord.ActionRowComponent c) | |||||
| { | |||||
| this.Type = c.Type; | |||||
| this.Components = c.Components?.Select(x => new ButtonComponent(x)).ToList(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,64 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class ButtonComponent | |||||
| { | |||||
| [JsonProperty("type")] | |||||
| public ComponentType Type { get; set; } | |||||
| [JsonProperty("style")] | |||||
| public ButtonStyle Style { get; set; } | |||||
| [JsonProperty("label")] | |||||
| public string Label { get; set; } | |||||
| [JsonProperty("emoji")] | |||||
| public Emoji Emote { get; set; } | |||||
| [JsonProperty("custom_id")] | |||||
| public string CustomId { get; set; } | |||||
| [JsonProperty("url")] | |||||
| public string Url { get; set; } | |||||
| [JsonProperty("disabled")] | |||||
| public bool Disabled { get; set; } | |||||
| public ButtonComponent() { } | |||||
| public ButtonComponent(Discord.ButtonComponent c) | |||||
| { | |||||
| this.Type = c.Type; | |||||
| this.Style = c.Style; | |||||
| this.Label = c.Label; | |||||
| this.CustomId = c.CustomId; | |||||
| this.Url = c.Url; | |||||
| this.Disabled = c.Disabled; | |||||
| if (c.Emote is Emote e) | |||||
| { | |||||
| this.Emote = new Emoji() | |||||
| { | |||||
| Name = e.Name, | |||||
| Animated = e.Animated, | |||||
| Id = e.Id, | |||||
| }; | |||||
| } | |||||
| else | |||||
| { | |||||
| this.Emote = new Emoji() | |||||
| { | |||||
| Name = c.Emote.Name | |||||
| }; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -26,7 +26,7 @@ namespace Discord.API | |||||
| public Optional<int> Flags { get; set; } | public Optional<int> Flags { get; set; } | ||||
| [JsonProperty("components")] | [JsonProperty("components")] | ||||
| public Optional<IMessageComponent[]> Components { get; set; } | |||||
| public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
| public InteractionApplicationCommandCallbackData() { } | public InteractionApplicationCommandCallbackData() { } | ||||
| public InteractionApplicationCommandCallbackData(string text) | public InteractionApplicationCommandCallbackData(string text) | ||||
| @@ -59,6 +59,6 @@ namespace Discord.API | |||||
| [JsonProperty("referenced_message")] | [JsonProperty("referenced_message")] | ||||
| public Optional<Message> ReferencedMessage { get; set; } | public Optional<Message> ReferencedMessage { get; set; } | ||||
| [JsonProperty("components")] | [JsonProperty("components")] | ||||
| public Optional<IMessageComponent[]> Components { get; set; } | |||||
| public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -25,7 +25,7 @@ namespace Discord.API.Rest | |||||
| public Optional<MessageReference> MessageReference { get; set; } | public Optional<MessageReference> MessageReference { get; set; } | ||||
| [JsonProperty("components")] | [JsonProperty("components")] | ||||
| public Optional<IMessageComponent[]> Components { get; set; } | |||||
| public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
| public CreateMessageParams(string content) | public CreateMessageParams(string content) | ||||
| { | { | ||||
| Content = content; | Content = content; | ||||
| @@ -31,7 +31,7 @@ namespace Discord.API.Rest | |||||
| public Optional<int> Flags { get; set; } | public Optional<int> Flags { get; set; } | ||||
| [JsonProperty("components")] | [JsonProperty("components")] | ||||
| public Optional<IMessageComponent[]> Components { get; set; } | |||||
| public Optional<API.ActionRowComponent[]> Components { get; set; } | |||||
| public CreateWebhookMessageParams(string content) | public CreateWebhookMessageParams(string content) | ||||
| { | { | ||||
| @@ -21,7 +21,7 @@ namespace Discord.API.Rest | |||||
| public Optional<Embed> Embed { get; set; } | public Optional<Embed> Embed { get; set; } | ||||
| public Optional<AllowedMentions> AllowedMentions { get; set; } | public Optional<AllowedMentions> AllowedMentions { get; set; } | ||||
| public Optional<MessageReference> MessageReference { get; set; } | public Optional<MessageReference> MessageReference { get; set; } | ||||
| public Optional<IMessageComponent[]> MessageComponent { get; set; } | |||||
| public Optional<ActionRowComponent[]> MessageComponent { get; set; } | |||||
| public bool IsSpoiler { get; set; } = false; | public bool IsSpoiler { get; set; } = false; | ||||
| public UploadFileParams(Stream file) | public UploadFileParams(Stream file) | ||||
| @@ -221,7 +221,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| } | } | ||||
| var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel(), AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel(), Components = component?.ToModel() }; | |||||
| var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel(), AllowedMentions = allowedMentions?.ToModel(), MessageReference = messageReference?.ToModel(), Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified }; | |||||
| var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
| } | } | ||||
| @@ -281,7 +281,7 @@ namespace Discord.Rest | |||||
| } | } | ||||
| } | } | ||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed?.ToModel() ?? Optional<API.Embed>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional<API.MessageReference>.Unspecified, MessageComponent = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified, IsSpoiler = isSpoiler }; | |||||
| var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS, Embed = embed?.ToModel() ?? Optional<API.Embed>.Unspecified, AllowedMentions = allowedMentions?.ToModel() ?? Optional<API.AllowedMentions>.Unspecified, MessageReference = messageReference?.ToModel() ?? Optional<API.MessageReference>.Unspecified, MessageComponent = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified, IsSpoiler = isSpoiler }; | |||||
| var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); | ||||
| return RestUserMessage.Create(client, channel, client.CurrentUser, model); | return RestUserMessage.Create(client, channel, client.CurrentUser, model); | ||||
| } | } | ||||
| @@ -129,9 +129,16 @@ namespace Discord.Rest | |||||
| if (model.Components.IsSpecified) | if (model.Components.IsSpecified) | ||||
| { | { | ||||
| Components = model.Components.Value.Select(x => | |||||
| (x as Newtonsoft.Json.Linq.JToken).ToObject<ActionRowComponent>() | |||||
| ).ToList(); | |||||
| Components = model.Components.Value.Select(x => new ActionRowComponent(x.Components.Select(x => | |||||
| new ButtonComponent( | |||||
| x.Style, | |||||
| x.Label, | |||||
| x.Emote.Id.HasValue ? new Emote(x.Emote.Id.Value, x.Emote.Name, x.Emote.Animated.GetValueOrDefault()) : new Emoji(x.Emote.Name), | |||||
| x.CustomId, | |||||
| x.Url, | |||||
| x.Disabled) | |||||
| ).ToList() | |||||
| )).ToList(); | |||||
| } | } | ||||
| else | else | ||||
| Components = new List<ActionRowComponent>(); | Components = new List<ActionRowComponent>(); | ||||
| @@ -94,7 +94,7 @@ namespace Discord.WebSocket | |||||
| ? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
| : Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
| TTS = isTTS, | TTS = isTTS, | ||||
| Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -134,7 +134,7 @@ namespace Discord.WebSocket | |||||
| Embeds = embed != null | Embeds = embed != null | ||||
| ? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
| : Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
| Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| }; | }; | ||||
| if (ephemeral) | if (ephemeral) | ||||
| @@ -110,7 +110,7 @@ namespace Discord.WebSocket | |||||
| ? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
| : Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
| TTS = isTTS, | TTS = isTTS, | ||||
| Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -149,7 +149,7 @@ namespace Discord.WebSocket | |||||
| Embeds = embed != null | Embeds = embed != null | ||||
| ? new API.Embed[] { embed.ToModel() } | ? new API.Embed[] { embed.ToModel() } | ||||
| : Optional<API.Embed[]>.Unspecified, | : Optional<API.Embed[]>.Unspecified, | ||||
| Components = component?.ToModel() ?? Optional<IMessageComponent[]>.Unspecified | |||||
| Components = component?.Components.Select(x => new API.ActionRowComponent(x)).ToArray() ?? Optional<API.ActionRowComponent[]>.Unspecified | |||||
| }; | }; | ||||
| if (ephemeral) | if (ephemeral) | ||||
| @@ -162,9 +162,16 @@ namespace Discord.WebSocket | |||||
| if (model.Components.IsSpecified) | if (model.Components.IsSpecified) | ||||
| { | { | ||||
| Components = model.Components.Value.Select(x => | |||||
| (x as Newtonsoft.Json.Linq.JToken).ToObject<ActionRowComponent>() | |||||
| ).ToList(); | |||||
| Components = model.Components.Value.Select(x => new ActionRowComponent(x.Components.Select(x => | |||||
| new ButtonComponent( | |||||
| x.Style, | |||||
| x.Label, | |||||
| x.Emote.Id.HasValue ? new Emote(x.Emote.Id.Value, x.Emote.Name, x.Emote.Animated.GetValueOrDefault()) : new Emoji(x.Emote.Name), | |||||
| x.CustomId, | |||||
| x.Url, | |||||
| x.Disabled) | |||||
| ).ToList() | |||||
| )).ToList(); | |||||
| } | } | ||||
| else | else | ||||
| Components = new List<ActionRowComponent>(); | Components = new List<ActionRowComponent>(); | ||||