Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Merge pull request #67 from nyxx-discord/dev
Browse files Browse the repository at this point in the history
Release 4.4.0
  • Loading branch information
l7ssha authored Nov 14, 2022
2 parents 1041107 + 7f4709d commit 101af14
Show file tree
Hide file tree
Showing 23 changed files with 497 additions and 84 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pubspec.lock
.packages
private_test.dart
.vscode/
doc/**
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.4.0
__14.11.022__

- feature: Add support for new select menus components (#62)

## 4.3.2
__09.11.2022__

Expand Down
4 changes: 2 additions & 2 deletions example/buttons_and_dropdowns.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ final singleCommand = SlashCommandBuilder("help", "This is example help command"
// Adding selects is as easy as adding buttons. Use MultiselectBuilder with custom id
// and list of multiselect options.
final firstRow = ComponentRowBuilder()
..addComponent(ButtonBuilder("This is button label", "thisisid", ComponentStyle.success))
..addComponent(ButtonBuilder("This is another button label", "thisisid2", ComponentStyle.success));
..addComponent(ButtonBuilder("This is button label", "thisisid", ButtonStyle.success))
..addComponent(ButtonBuilder("This is another button label", "thisisid2", ButtonStyle.primary));
final secondRow = ComponentRowBuilder()
..addComponent(MultiselectBuilder("customId", [
MultiselectOptionBuilder("example option 1", "option1"),
Expand Down
27 changes: 23 additions & 4 deletions lib/nyxx_interactions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export 'src/builders/component_builder.dart'
MultiselectBuilder,
MultiselectOptionBuilder,
TextInputBuilder,
TextInputStyle;
TextInputStyle,
UserMultiSelectBuilder,
RoleMultiSelectBuilder,
MentionableMultiSelectBuilder,
ChannelMultiSelectBuilder;
export 'src/builders/modal_builder.dart' show ModalBuilder;
export 'src/builders/slash_command_builder.dart' show SlashCommandBuilder;
export 'src/events/interaction_event.dart'
Expand All @@ -27,7 +31,11 @@ export 'src/events/interaction_event.dart'
InteractionEventWithAcknowledge,
ISlashCommandInteractionEvent,
IModalResponseMixin,
IModalInteractionEvent;
IModalInteractionEvent,
IUserMultiSelectInteractionEvent,
IRoleMultiSelectInteractionEvent,
IMentionableMultiSelectInteractionEvent,
IChannelMultiSelectInteractionEvent;
export 'src/exceptions/already_responded.dart' show AlreadyRespondedError;
export 'src/exceptions/interaction_expired.dart' show InteractionExpiredError;
export 'src/exceptions/response_required.dart' show ResponseRequiredError;
Expand All @@ -40,8 +48,19 @@ export 'src/internal/utils.dart' show slashCommandNameRegex;
export 'src/models/arg_choice.dart' show IArgChoice;
export 'src/models/command_option.dart' show ICommandOption, CommandOptionType;
export 'src/models/interaction.dart'
show IComponentInteraction, IInteraction, IButtonInteraction, IMultiselectInteraction, ISlashCommandInteraction, IModalInteraction;
export 'src/models/interaction_data_resolved.dart' show IInteractionDataResolved, IPartialChannel;
show
IComponentInteraction,
IInteraction,
IButtonInteraction,
IMultiselectInteraction,
ISlashCommandInteraction,
IModalInteraction,
IUserMultiSelectInteraction,
IRoleMultiSelectInteraction,
IMentionableMultiSelectInteraction,
IChannelMultiSelectInteraction,
IResolvedSelectInteraction;
export 'src/models/interaction_data_resolved.dart' show IInteractionDataResolved, IPartialChannel, IInteractionSlashDataResolved;
export 'src/models/interaction_option.dart' show IInteractionOption;
export 'src/models/slash_command_permission.dart' show ISlashCommandPermissionOverride, ISlashCommandPermissionOverrides, SlashCommandPermissionType;
export 'src/models/slash_command.dart' show ISlashCommand;
Expand Down
108 changes: 78 additions & 30 deletions lib/src/builders/component_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,41 @@ abstract class ComponentBuilderAbstract extends Builder {
};
}

/// Allows to create multi select option for [MultiselectBuilder]
/// Abstract base class that represents any multi select builer.
abstract class MultiSelectBuilderAbstract extends ComponentBuilderAbstract {
/// Id for the select menu; max 100 characters.
final String customId;

/// Placeholder text if nothing is selected; max 150 characters.
String? placeholder;

/// Minimum number of items that must be chosen (defaults to 1); min 0, max 25.
int? minValues;

/// Maximum number of items that can be chosen (defaults to 1); max 25.
int? maxValues;

/// Whether select menu is disabled (defaults to `false`).
bool? disabled;

MultiSelectBuilderAbstract(this.customId) {
if (customId.length > 100) {
throw ArgumentError("Custom Id for Select cannot have more than 100 characters");
}
}

@override
Map<String, dynamic> build() => {
...super.build(),
'custom_id': customId,
if (placeholder != null) 'placeholder': placeholder,
if (minValues != null) 'min_values': minValues,
if (maxValues != null) 'max_values': maxValues,
if (disabled != null) 'disabled': disabled,
};
}

/// Allows to create multi select options for [MultiselectBuilder].
class MultiselectOptionBuilder extends Builder {
/// User-facing name of the option
final String label;
Expand Down Expand Up @@ -47,36 +81,15 @@ class MultiselectOptionBuilder extends Builder {
}

/// Allows to create multi select interactive components.
class MultiselectBuilder extends ComponentBuilderAbstract {
class MultiselectBuilder extends MultiSelectBuilderAbstract {
@override
ComponentType get type => ComponentType.select;

/// Max: 100 characters
final String customId;
ComponentType get type => ComponentType.multiSelect;

/// Max: 25
final List<MultiselectOptionBuilder> options = [];

/// Custom placeholder when nothing selected
String? placeholder;

/// Minimum number of options that can be chosen.
/// Default: 1, min: 1, max: 25
int? minValues;

/// Maximum numbers of options that can be chosen
/// Default: 1, min: 1, max: 25
int? maxValues;

/// Whether disable the select menu.
bool? disabled;

/// Creates instance of [MultiselectBuilder]
MultiselectBuilder(this.customId, [Iterable<MultiselectOptionBuilder>? options]) {
if (customId.length > 100) {
throw ArgumentError("Custom Id for Select cannot have more than 100 characters");
}

MultiselectBuilder(super.customId, [Iterable<MultiselectOptionBuilder>? options]) {
if (options != null) {
this.options.addAll(options);
}
Expand All @@ -88,12 +101,47 @@ class MultiselectBuilder extends ComponentBuilderAbstract {
@override
Map<String, dynamic> build() => {
...super.build(),
"custom_id": customId,
"options": [for (final optionBuilder in options) optionBuilder.build()],
if (placeholder != null) "placeholder": placeholder,
if (minValues != null) "min_values": minValues,
if (maxValues != null) "max_values": maxValues,
if (disabled != null) "disabled": disabled,
};
}

/// Builder to create select menu with [IUser]s inside of it.
class UserMultiSelectBuilder extends MultiSelectBuilderAbstract {
@override
ComponentType get type => ComponentType.userMultiSelect;

UserMultiSelectBuilder(super.customId);
}

/// Builder to create select menu with [IRole]s inside of it.
class RoleMultiSelectBuilder extends MultiSelectBuilderAbstract {
@override
ComponentType get type => ComponentType.roleMultiSelect;

RoleMultiSelectBuilder(super.customId);
}

/// Builder to create select menu with mentionables ([IRole]s & [IUser]s) inside of it.
class MentionableMultiSelectBuilder extends MultiSelectBuilderAbstract {
@override
ComponentType get type => ComponentType.mentionableMultiSelect;

MentionableMultiSelectBuilder(super.customId);
}

/// Builder to create select menu with [IChannel]s inside of it.
class ChannelMultiSelectBuilder extends MultiSelectBuilderAbstract {
@override
ComponentType get type => ComponentType.channelMultiSelect;

List<ChannelType>? channelTypes;

ChannelMultiSelectBuilder(super.customId, [this.channelTypes]);

@override
Map<String, dynamic> build() => {
...super.build(),
if (channelTypes != null) 'channel_types': channelTypes!.map((e) => e.value).toList(),
};
}

Expand Down
45 changes: 45 additions & 0 deletions lib/src/events/interaction_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,51 @@ class MultiselectInteractionEvent extends ComponentInteractionEvent<IMultiselect
}
}

abstract class IUserMultiSelectInteractionEvent implements ComponentInteractionEvent<IUserMultiSelectInteraction> {}

class UserMultiSelectInteractionEvent extends ComponentInteractionEvent<IUserMultiSelectInteraction> implements IUserMultiSelectInteractionEvent {
@override
late final IUserMultiSelectInteraction interaction;

UserMultiSelectInteractionEvent(Interactions interactions, RawApiMap raw) : super(interactions, raw) {
interaction = UserMultiSelectInteraction(client, raw);
}
}

abstract class IRoleMultiSelectInteractionEvent implements ComponentInteractionEvent<IRoleMultiSelectInteraction> {}

class RoleMultiSelectInteractionEvent extends ComponentInteractionEvent<IRoleMultiSelectInteraction> implements IRoleMultiSelectInteractionEvent {
@override
late final IRoleMultiSelectInteraction interaction;

RoleMultiSelectInteractionEvent(Interactions interactions, RawApiMap raw) : super(interactions, raw) {
interaction = RoleMultiSelectInteraction(client, raw);
}
}

abstract class IMentionableMultiSelectInteractionEvent implements ComponentInteractionEvent<IMentionableMultiSelectInteraction> {}

class MentionableMultiSelectInteractionEvent extends ComponentInteractionEvent<IMentionableMultiSelectInteraction>
implements IMentionableMultiSelectInteractionEvent {
@override
late final IMentionableMultiSelectInteraction interaction;

MentionableMultiSelectInteractionEvent(Interactions interactions, RawApiMap raw) : super(interactions, raw) {
interaction = MentionableMultiSelectInteraction(client, raw);
}
}

abstract class IChannelMultiSelectInteractionEvent implements ComponentInteractionEvent<IChannelMultiSelectInteraction> {}

class ChannelMultiSelectInteractionEvent extends ComponentInteractionEvent<IChannelMultiSelectInteraction> implements IChannelMultiSelectInteractionEvent {
@override
late final IChannelMultiSelectInteraction interaction;

ChannelMultiSelectInteractionEvent(Interactions interactions, RawApiMap raw) : super(interactions, raw) {
interaction = ChannelMultiSelectInteraction(client, raw);
}
}

mixin IModalResponseMixin {
IInteractions get interactions;
IInteraction get interaction;
Expand Down
90 changes: 90 additions & 0 deletions lib/src/interactions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ abstract class IInteractions {
/// Register callback for dropdown event for given [id]
void registerMultiselectHandler(String id, MultiselectInteractionHandler handler);

/// Register callback for user dropdown event for given [id].
void registerUserMultiSelectHandler(String id, UserMultiSelectInteractionHandler handler);

/// Register callback for role dropdown event for given [id].
void registerRoleMultiSelectHandler(String id, RoleMultiSelectInteractionHandler handler);

/// Register callback for mentionable dropdown event for given [id].
void registerMentionableMultiSelectHandler(String id, MentionableMultiSelectInteractionHandler handler);

/// Register callback for channel dropdown event for given [id].
void registerChannelMultiSelectHandler(String id, ChannelMultiSelectInteractionHandler handler);

/// Allows to register new [SlashCommandBuilder]
void registerSlashCommand(SlashCommandBuilder slashCommandBuilder);

Expand Down Expand Up @@ -89,6 +101,10 @@ class Interactions implements IInteractions {
final _buttonHandlers = <String, ButtonInteractionHandler>{};
final _autocompleteHandlers = <String, AutocompleteInteractionHandler>{};
final _multiselectHandlers = <String, MultiselectInteractionHandler>{};
final _userMultiSelectHandlers = <String, UserMultiSelectInteractionHandler>{};
final _roleMultiSelectHandlers = <String, RoleMultiSelectInteractionHandler>{};
final _mentionableMultiSelectHandlers = <String, MentionableMultiSelectInteractionHandler>{};
final _channelMultiSelectHandlers = <String, ChannelMultiSelectInteractionHandler>{};

final permissionOverridesCache = <Snowflake, Map<Snowflake, SlashCommandPermissionOverrides>>{};

Expand Down Expand Up @@ -133,12 +149,30 @@ class Interactions implements IInteractions {
final componentType = rawData["d"]["data"]["component_type"] as int;

switch (componentType) {
// ComponentType.button
case 2:
(events as EventController).onButtonEventController.add(ButtonInteractionEvent(this, rawData["d"] as Map<String, dynamic>));
break;
// ComponentType.select
case 3:
(events as EventController).onMultiselectEventController.add(MultiselectInteractionEvent(this, rawData["d"] as Map<String, dynamic>));
break;
// ComponentType.userMultiSelect
case 5:
(events as EventController).onUserMultiSelectController.add(UserMultiSelectInteractionEvent(this, rawData['d'] as RawApiMap));
break;
// ComponentType.roleMultiSelect
case 6:
(events as EventController).onRoleMultiSelectController.add(RoleMultiSelectInteractionEvent(this, rawData['d'] as RawApiMap));
break;
// ComponentType.mentionableMultiSelect
case 7:
(events as EventController).onMentionableMultiSelectController.add(MentionableMultiSelectInteractionEvent(this, rawData['d'] as RawApiMap));
break;
// ComponentType.channelMultiSelect
case 8:
(events as EventController).onChannelMultiSelectController.add(ChannelMultiSelectInteractionEvent(this, rawData['d'] as RawApiMap));
break;
default:
_logger.warning("Unknown componentType type: [$componentType]; Payload: ${jsonEncode(rawData)}");
}
Expand Down Expand Up @@ -254,6 +288,50 @@ class Interactions implements IInteractions {
});
}

if (_userMultiSelectHandlers.isNotEmpty) {
events.onUserMultiSelect.listen((event) {
if (_userMultiSelectHandlers.containsKey(event.interaction.customId)) {
_logger.info("Executing user select with id [${event.interaction.customId}]");
_userMultiSelectHandlers[event.interaction.customId]!(event);
} else {
_logger.warning("Received event for unknown user select: ${event.interaction.customId}");
}
});
}

if (_roleMultiSelectHandlers.isNotEmpty) {
events.onRoleMultiSelect.listen((event) {
if (_roleMultiSelectHandlers.containsKey(event.interaction.customId)) {
_logger.info("Executing role select with id [${event.interaction.customId}]");
_roleMultiSelectHandlers[event.interaction.customId]!(event);
} else {
_logger.warning("Received event for unknown role select: ${event.interaction.customId}");
}
});
}

if (_mentionableMultiSelectHandlers.isNotEmpty) {
events.onMentionableMultiSelect.listen((event) {
if (_mentionableMultiSelectHandlers.containsKey(event.interaction.customId)) {
_logger.info("Executing mentionable select with id [${event.interaction.customId}]");
_mentionableMultiSelectHandlers[event.interaction.customId]!(event);
} else {
_logger.warning("Received event for unknown mentionable select: ${event.interaction.customId}");
}
});
}

if (_channelMultiSelectHandlers.isNotEmpty) {
events.onChannelMultiSelect.listen((event) {
if (_channelMultiSelectHandlers.containsKey(event.interaction.customId)) {
_logger.info("Executing channel select with id [${event.interaction.customId}]");
_channelMultiSelectHandlers[event.interaction.customId]!(event);
} else {
_logger.warning("Received event for unknown channel select: ${event.interaction.customId}");
}
});
}

if (_autocompleteHandlers.isNotEmpty) {
events.onAutocompleteEvent.listen((event) {
final name = event.focusedOption.name;
Expand Down Expand Up @@ -289,6 +367,18 @@ class Interactions implements IInteractions {
@override
void registerSlashCommandHandler(String id, SlashCommandHandler handler) => _commandHandlers[id] = handler;

@override
void registerRoleMultiSelectHandler(String id, RoleMultiSelectInteractionHandler handler) => _roleMultiSelectHandlers[id] = handler;

@override
void registerUserMultiSelectHandler(String id, UserMultiSelectInteractionHandler handler) => _userMultiSelectHandlers[id] = handler;

@override
void registerMentionableMultiSelectHandler(String id, MentionableMultiSelectInteractionHandler handler) => _mentionableMultiSelectHandlers[id] = handler;

@override
void registerChannelMultiSelectHandler(String id, ChannelMultiSelectInteractionHandler handler) => _channelMultiSelectHandlers[id] = handler;

/// Deletes global command
@override
Future<void> deleteGlobalCommand(Snowflake commandId) => interactionsEndpoints.deleteGlobalCommand(client.appId, commandId);
Expand Down
Loading

0 comments on commit 101af14

Please sign in to comment.