diff --git a/lib/nyxx.dart b/lib/nyxx.dart index 3d85fa595..c1b3b92cd 100644 --- a/lib/nyxx.dart +++ b/lib/nyxx.dart @@ -151,6 +151,8 @@ export 'src/models/message/component.dart' MessageComponent, SelectMenuComponent, SelectMenuOption, + SelectMenuDefaultValue, + SelectMenuDefaultValueType, TextInputComponent, ButtonStyle, MessageComponentType, diff --git a/lib/src/http/managers/message_manager.dart b/lib/src/http/managers/message_manager.dart index 784648e58..86a29422d 100644 --- a/lib/src/http/managers/message_manager.dart +++ b/lib/src/http/managers/message_manager.dart @@ -274,6 +274,7 @@ class MessageManager extends Manager { options: maybeParseMany(raw['options'], parseSelectMenuOption), channelTypes: maybeParseMany(raw['channel_types'], ChannelType.parse), placeholder: raw['placeholder'] as String?, + defaultValues: maybeParseMany(raw['default_values'], parseSelectMenuDefaultValue), minValues: raw['min_values'] as int?, maxValues: raw['max_values'] as int?, isDisabled: raw['disabled'] as bool?, @@ -291,6 +292,13 @@ class MessageManager extends Manager { ); } + SelectMenuDefaultValue parseSelectMenuDefaultValue(Map raw) { + return SelectMenuDefaultValue( + id: Snowflake.parse(raw['id']!), + type: SelectMenuDefaultValueType.parse(raw['type'] as String), + ); + } + MessageInteraction parseMessageInteraction(Map raw, {Snowflake? guildId}) { final user = client.users.parse(raw['user'] as Map); diff --git a/lib/src/models/message/component.dart b/lib/src/models/message/component.dart index 3b6ab40d2..68efa8f9f 100644 --- a/lib/src/models/message/component.dart +++ b/lib/src/models/message/component.dart @@ -1,7 +1,9 @@ import 'package:nyxx/src/models/channel/channel.dart'; import 'package:nyxx/src/models/emoji.dart'; +import 'package:nyxx/src/models/snowflake.dart'; import 'package:nyxx/src/utils/to_string_helper/to_string_helper.dart'; +/// The type of a [MessageComponent]. enum MessageComponentType { actionRow._(1), button._(2), @@ -12,10 +14,14 @@ enum MessageComponentType { mentionableSelect._(7), channelSelect._(8); + /// The value of this [MessageComponentType]. final int value; const MessageComponentType._(this.value); + /// Parse a [MessageComponentType] from an [int]. + /// + /// The [value] must be a valid message component type. factory MessageComponentType.parse(int value) => MessageComponentType.values.firstWhere( (type) => type.value == value, orElse: () => throw FormatException('Unknown message component type', value), @@ -25,35 +31,48 @@ enum MessageComponentType { String toString() => 'MessageComponentType($value)'; } +/// A component in a [Message]. abstract class MessageComponent with ToStringHelper { + /// The type of this component. MessageComponentType get type; } +/// A [MessageComponent] that contains multiple child [MessageComponent]s. class ActionRowComponent extends MessageComponent { @override MessageComponentType get type => MessageComponentType.actionRow; + /// The children of this [ActionRow]. final List components; + /// Create a new [ActionRowComponent]. ActionRowComponent({required this.components}); } +/// A clickable button. class ButtonComponent extends MessageComponent { @override MessageComponentType get type => MessageComponentType.button; + /// The style of this button. final ButtonStyle style; + /// The label displayed on this button. final String? label; + /// The [Emoji] displayed on this button. final Emoji? emoji; + /// This component's custom ID. final String? customId; + /// The URL this button redirects to, if this button is a URL button. final Uri? url; + /// Whether this button is disabled. final bool? isDisabled; + /// Create a new [ButtonComponent]. ButtonComponent({ required this.style, required this.label, @@ -64,6 +83,7 @@ class ButtonComponent extends MessageComponent { }); } +/// The style of a [ButtonComponent]. enum ButtonStyle { primary._(1), secondary._(2), @@ -71,10 +91,14 @@ enum ButtonStyle { danger._(4), link._(5); + /// The value of this [ButtonStyle]. final int value; const ButtonStyle._(this.value); + /// Parse a [ButtonStyle] from an [int]. + /// + /// The [value] must be a valid button style. factory ButtonStyle.parse(int value) => ButtonStyle.values.firstWhere( (style) => style.value == value, orElse: () => throw FormatException('Unknown button style', value), @@ -84,47 +108,103 @@ enum ButtonStyle { String toString() => 'ButtonStyle($value)'; } +/// A dropdown menu in which users can select from on or more choices. class SelectMenuComponent extends MessageComponent { @override final MessageComponentType type; + /// This component's custom ID. final String customId; + /// The options in this menu. + /// + /// Will be `null` if this menu is not a [MessageComponentType.stringSelect] menu. final List? options; + /// The channel types displayed in this select menu. + /// + /// Will be `null` if this menu is not a [MessageComponentType.channelSelect] menu. final List? channelTypes; + /// The placeholder shown when the user has not yet selected a value. final String? placeholder; + /// The default selected values in this menu. + final List? defaultValues; + + /// The minimum number of values the user must select. final int? minValues; + /// The maximum number of values the user must select. final int? maxValues; + /// Whether this component is disabled. final bool? isDisabled; + /// Create a new [SelectMenuComponent]. SelectMenuComponent({ required this.type, required this.customId, required this.options, required this.channelTypes, required this.placeholder, + required this.defaultValues, required this.minValues, required this.maxValues, required this.isDisabled, }); } +/// The type of a [SelectMenuDefaultValue]. +enum SelectMenuDefaultValueType { + user._('user'), + role._('role'), + channel._('channel'); + + /// The value of this [SelectMenuDefaultValue]. + final String value; + + const SelectMenuDefaultValueType._(this.value); + + /// Parse a [SelectMenuDefaultValueType] from a [String]. + /// + /// The [value] must be a valid select menu default value type. + factory SelectMenuDefaultValueType.parse(String value) => SelectMenuDefaultValueType.values.firstWhere( + (type) => type.value == value, + orElse: () => throw FormatException('Unknown select menu default value type', value), + ); +} + +/// A default value in a [SelectMenu]. +class SelectMenuDefaultValue { + /// The ID of this entity. + final Snowflake id; + + /// The type of this entity. + final SelectMenuDefaultValueType type; + + /// Create a new [SelectMenuDefaultValue]. + SelectMenuDefaultValue({required this.id, required this.type}); +} + +/// An option in a [SelectMenu]. class SelectMenuOption with ToStringHelper { + /// The label shown to the user. final String label; + /// The value sent to the application. final String value; + /// The description of this option. final String? description; + /// The emoji shown by this emoji. final Emoji? emoji; + /// Whether this [SelectMenuOption] is selected by default. final bool? isDefault; + /// Create a new [SelectMenuOption]. SelectMenuOption({ required this.label, required this.value, @@ -134,26 +214,36 @@ class SelectMenuOption with ToStringHelper { }); } +/// A text field in a modal. class TextInputComponent extends MessageComponent { @override MessageComponentType get type => MessageComponentType.textInput; + /// This component's custom ID. final String customId; + /// The style of this [TextInputComponent]. final TextInputStyle? style; + /// This component's label. final String? label; + /// The minimum number of characters the user must input. final int? minLength; + /// The maximum number of characters the user can input. final int? maxLength; + /// Whether this component requires input. final bool? isRequired; + /// The text contained in this component. final String? value; + /// Placeholder text shown when this component is empty. final String? placeholder; + /// Create a new [TextInputComponent]. TextInputComponent({ required this.customId, required this.style, @@ -166,14 +256,19 @@ class TextInputComponent extends MessageComponent { }); } +/// The type of a [TextInputComponent]. enum TextInputStyle { short._(1), paragraph._(2); + /// The value of this [TextInputStyle]. final int value; const TextInputStyle._(this.value); + /// Parse a [TextInputComponent] from an [int]. + /// + /// The [value] must beb a valid text input style. factory TextInputStyle.parse(int value) => TextInputStyle.values.firstWhere( (style) => style.value == value, orElse: () => throw FormatException('Unknown text input style', value),