getMultiSelection<T> method

  1. @override
Future<List<T>> getMultiSelection<T>(
  1. List<T> choices,
  2. MessageBuilder builder,
  3. {ResponseLevel? level,
  4. Duration? timeout,
  5. bool authorOnly = true,
  6. FutureOr<SelectMenuOptionBuilder> toSelectMenuOption(
    1. T
    )?,
  7. Converter<T>? converterOverride}
)
inherited

Present the user with a drop-down menu of choices and return the selected choices.

If timeout is set, this method will complete with an error after timeout has passed.

If authorOnly is set, only the author of this interaction will be able to interact with a button.

level will change the level at which the message is sent, similarly to respond.

converterOverride can be set to change how each value is converted to a multi-select option. The default is to use Converter.toSelectMenuOption on the default converter for T.

You might also be interested in:

Implementation

@override
Future<List<T>> getMultiSelection<T>(
  List<T> choices,
  MessageBuilder builder, {
  ResponseLevel? level,
  Duration? timeout,
  bool authorOnly = true,
  FutureOr<SelectMenuOptionBuilder> Function(T)? toSelectMenuOption,
  Converter<T>? converterOverride,
}) async {
  if (_delegate != null) {
    return _delegate!.getMultiSelection(
      choices,
      builder,
      authorOnly: authorOnly,
      converterOverride: converterOverride,
      level: level,
      timeout: timeout,
      toSelectMenuOption: toSelectMenuOption,
    );
  }

  toSelectMenuOption ??= converterOverride?.toSelectMenuOption;
  toSelectMenuOption ??= commands.getConverter(RuntimeType<T>())?.toSelectMenuOption;

  if (toSelectMenuOption == null) {
    throw UncaughtCommandsException(
      'No suitable method for converting $T to SelectMenuOptionBuilder found',
      _nearestCommandContext,
    );
  }

  Map<String, T> idToValue = {};
  List<SelectMenuOptionBuilder> options = await Future.wait(choices.map(
    (value) async {
      SelectMenuOptionBuilder builder = await toSelectMenuOption!(value);
      idToValue[builder.value] = value;
      return builder;
    },
  ));

  ComponentId menuId = ComponentId.generate(
    expirationTime: timeout,
    allowedUser: authorOnly ? user.id : null,
  );

  SelectMenuBuilder menu = SelectMenuBuilder(
    type: MessageComponentType.stringSelect,
    customId: menuId.toString(),
    options: options,
    maxValues: choices.length,
  );
  ActionRowBuilder row = ActionRowBuilder(components: [menu]);

  (builder.components ??= []).add(row);

  Message message = await respond(builder, level: level);

  try {
    SelectMenuContext<List<String>> context =
        await commands.eventManager.nextSelectMenuEvent(menuId);

    context._parent = this;
    _delegate = context;

    for (final value in context.selected) {
      final matchingOptionIndex = menu.options!.indexWhere((option) => option.value == value);

      if (matchingOptionIndex >= 0) {
        menu.options![matchingOptionIndex] = SelectMenuOptionBuilder(
          label: menu.options![matchingOptionIndex].label,
          value: value,
        );
      }
    }

    return context.selected.map((id) => idToValue[id]!).toList();
  } on TimeoutException catch (e, s) {
    throw InteractionTimeoutException(
      'TImed out waiting for selection',
      _nearestCommandContext,
    )..stackTrace = s;
  } finally {
    menu.isDisabled = true;
    await _updateMessage(this, message, MessageCreateUpdateBuilder.fromMessageBuilder(builder));
  }
}