mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 16:43:31 +01:00
feat(modals): modals, input text components and modal submits, v13 style (#7431)
This commit is contained in:
@@ -196,6 +196,8 @@ class BaseCommandInteraction extends Interaction {
|
||||
editReply() {}
|
||||
deleteReply() {}
|
||||
followUp() {}
|
||||
showModal() {}
|
||||
awaitModalSubmit() {}
|
||||
}
|
||||
|
||||
InteractionResponses.applyToClass(BaseCommandInteraction, ['deferUpdate', 'update']);
|
||||
|
||||
@@ -4,7 +4,7 @@ const { TypeError } = require('../errors');
|
||||
const { MessageComponentTypes, Events } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents an interactive component of a Message. It should not be necessary to construct this directly.
|
||||
* Represents an interactive component of a Message or Modal. It should not be necessary to construct this directly.
|
||||
* See {@link MessageComponent}
|
||||
*/
|
||||
class BaseMessageComponent {
|
||||
@@ -15,18 +15,20 @@ class BaseMessageComponent {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data that can be resolved into options for a MessageComponent. This can be:
|
||||
* Data that can be resolved into options for a component. This can be:
|
||||
* * MessageActionRowOptions
|
||||
* * MessageButtonOptions
|
||||
* * MessageSelectMenuOptions
|
||||
* * TextInputComponentOptions
|
||||
* @typedef {MessageActionRowOptions|MessageButtonOptions|MessageSelectMenuOptions} MessageComponentOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Components that can be sent in a message. These can be:
|
||||
* Components that can be sent in a payload. These can be:
|
||||
* * MessageActionRow
|
||||
* * MessageButton
|
||||
* * MessageSelectMenu
|
||||
* * TextInputComponent
|
||||
* @typedef {MessageActionRow|MessageButton|MessageSelectMenu} MessageComponent
|
||||
* @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types}
|
||||
*/
|
||||
@@ -51,10 +53,10 @@ class BaseMessageComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MessageComponent based on the type of the incoming data
|
||||
* Constructs a component based on the type of the incoming data
|
||||
* @param {MessageComponentOptions} data Data for a MessageComponent
|
||||
* @param {Client|WebhookClient} [client] Client constructing this component
|
||||
* @returns {?MessageComponent}
|
||||
* @returns {?(MessageComponent|ModalComponent)}
|
||||
* @private
|
||||
*/
|
||||
static create(data, client) {
|
||||
@@ -79,6 +81,11 @@ class BaseMessageComponent {
|
||||
component = data instanceof MessageSelectMenu ? data : new MessageSelectMenu(data);
|
||||
break;
|
||||
}
|
||||
case MessageComponentTypes.TEXT_INPUT: {
|
||||
const TextInputComponent = require('./TextInputComponent');
|
||||
component = data instanceof TextInputComponent ? data : new TextInputComponent(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (client) {
|
||||
client.emit(Events.DEBUG, `[BaseMessageComponent] Received component with unknown type: ${data.type}`);
|
||||
@@ -90,7 +97,7 @@ class BaseMessageComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the type of a MessageComponent
|
||||
* Resolves the type of a component
|
||||
* @param {MessageComponentTypeResolvable} type The type to resolve
|
||||
* @returns {MessageComponentType}
|
||||
* @private
|
||||
|
||||
@@ -173,6 +173,14 @@ class Interaction extends Base {
|
||||
return InteractionTypes[this.type] === InteractionTypes.APPLICATION_COMMAND && typeof this.targetId !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this interaction is a {@link ModalSubmitInteraction}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isModalSubmit() {
|
||||
return InteractionTypes[this.type] === InteractionTypes.MODAL_SUBMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this interaction is a {@link UserContextMenuInteraction}
|
||||
* @returns {boolean}
|
||||
|
||||
@@ -12,14 +12,16 @@ class MessageActionRow extends BaseMessageComponent {
|
||||
* Components that can be placed in an action row
|
||||
* * MessageButton
|
||||
* * MessageSelectMenu
|
||||
* @typedef {MessageButton|MessageSelectMenu} MessageActionRowComponent
|
||||
* * TextInputComponent
|
||||
* @typedef {MessageButton|MessageSelectMenu|TextInputComponent} MessageActionRowComponent
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for components that can be placed in an action row
|
||||
* * MessageButtonOptions
|
||||
* * MessageSelectMenuOptions
|
||||
* @typedef {MessageButtonOptions|MessageSelectMenuOptions} MessageActionRowComponentOptions
|
||||
* * TextInputComponentOptions
|
||||
* @typedef {MessageButtonOptions|MessageSelectMenuOptions|TextInputComponentOptions} MessageActionRowComponentOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,6 +101,8 @@ class MessageComponentInteraction extends Interaction {
|
||||
followUp() {}
|
||||
deferUpdate() {}
|
||||
update() {}
|
||||
showModal() {}
|
||||
awaitModalSubmit() {}
|
||||
}
|
||||
|
||||
InteractionResponses.applyToClass(MessageComponentInteraction);
|
||||
|
||||
103
src/structures/Modal.js
Normal file
103
src/structures/Modal.js
Normal file
@@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
|
||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a modal (form) to be shown in response to an interaction
|
||||
*/
|
||||
class Modal {
|
||||
/**
|
||||
* @typedef {Object} ModalOptions
|
||||
* @property {string} [customId] A unique string to be sent in the interaction when clicked
|
||||
* @property {string} [title] The title to be displayed on this modal
|
||||
* @property {MessageActionRow[]|MessageActionRowOptions[]} [components]
|
||||
* Action rows containing interactive components for the modal (text input components)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Modal|ModalOptions} data Modal to clone or raw data
|
||||
* @param {Client} client The client constructing this Modal, if provided
|
||||
*/
|
||||
constructor(data = {}, client = null) {
|
||||
/**
|
||||
* A list of MessageActionRows in the modal
|
||||
* @type {MessageActionRow[]}
|
||||
*/
|
||||
this.components = data.components?.map(c => BaseMessageComponent.create(c, client)) ?? [];
|
||||
|
||||
/**
|
||||
* A unique string to be sent in the interaction when submitted
|
||||
* @type {?string}
|
||||
*/
|
||||
this.customId = data.custom_id ?? data.customId ?? null;
|
||||
|
||||
/**
|
||||
* The title to be displayed on this modal
|
||||
* @type {?string}
|
||||
*/
|
||||
this.title = data.title ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds components to the modal.
|
||||
* @param {...MessageActionRowResolvable[]} components The components to add
|
||||
* @returns {Modal}
|
||||
*/
|
||||
addComponents(...components) {
|
||||
this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the components of the modal.
|
||||
* @param {...MessageActionRowResolvable[]} components The components to set
|
||||
* @returns {Modal}
|
||||
*/
|
||||
setComponents(...components) {
|
||||
this.spliceComponents(0, this.components.length, components);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom id for this modal
|
||||
* @param {string} customId A unique string to be sent in the interaction when submitted
|
||||
* @returns {Modal}
|
||||
*/
|
||||
setCustomId(customId) {
|
||||
this.customId = Util.verifyString(customId, RangeError, 'MODAL_CUSTOM_ID');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, and inserts components in the modal.
|
||||
* @param {number} index The index to start at
|
||||
* @param {number} deleteCount The number of components to remove
|
||||
* @param {...MessageActionRowResolvable[]} [components] The replacing components
|
||||
* @returns {Modal}
|
||||
*/
|
||||
spliceComponents(index, deleteCount, ...components) {
|
||||
this.components.splice(index, deleteCount, ...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of this modal
|
||||
* @param {string} title The title to be displayed on this modal
|
||||
* @returns {Modal}
|
||||
*/
|
||||
setTitle(title) {
|
||||
this.title = Util.verifyString(title, RangeError, 'MODAL_TITLE');
|
||||
return this;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
components: this.components.map(c => c.toJSON()),
|
||||
custom_id: this.customId,
|
||||
title: this.title,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Modal;
|
||||
53
src/structures/ModalSubmitFieldsResolver.js
Normal file
53
src/structures/ModalSubmitFieldsResolver.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const { TypeError } = require('../errors');
|
||||
const { MessageComponentTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* A resolver for modal submit interaction text inputs.
|
||||
*/
|
||||
class ModalSubmitFieldsResolver {
|
||||
constructor(components) {
|
||||
/**
|
||||
* The components within the modal
|
||||
* @type {PartialModalActionRow[]} The components in the modal
|
||||
*/
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extracted fields from the modal
|
||||
* @type {PartialInputTextData[]} The fields in the modal
|
||||
* @private
|
||||
*/
|
||||
get _fields() {
|
||||
return this.components.reduce((previous, next) => previous.concat(next.components), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a field given a custom id from a component
|
||||
* @param {string} customId The custom id of the component
|
||||
* @returns {?PartialInputTextData}
|
||||
*/
|
||||
getField(customId) {
|
||||
const field = this._fields.find(f => f.customId === customId);
|
||||
if (!field) throw new TypeError('MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND', customId);
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a text input component given a custom id
|
||||
* @param {string} customId The custom id of the text input component
|
||||
* @returns {?string}
|
||||
*/
|
||||
getTextInputValue(customId) {
|
||||
const field = this.getField(customId);
|
||||
const expectedType = MessageComponentTypes[MessageComponentTypes.TEXT_INPUT];
|
||||
if (field.type !== expectedType) {
|
||||
throw new TypeError('MODAL_SUBMIT_INTERACTION_FIELD_TYPE', customId, field.type, expectedType);
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ModalSubmitFieldsResolver;
|
||||
111
src/structures/ModalSubmitInteraction.js
Normal file
111
src/structures/ModalSubmitInteraction.js
Normal file
@@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
|
||||
const Interaction = require('./Interaction');
|
||||
const InteractionWebhook = require('./InteractionWebhook');
|
||||
const ModalSubmitFieldsResolver = require('./ModalSubmitFieldsResolver');
|
||||
const InteractionResponses = require('./interfaces/InteractionResponses');
|
||||
const { MessageComponentTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a modal submit interaction.
|
||||
* @extends {Interaction}
|
||||
* @implements {InteractionResponses}
|
||||
*/
|
||||
class ModalSubmitInteraction extends Interaction {
|
||||
constructor(client, data) {
|
||||
super(client, data);
|
||||
|
||||
/**
|
||||
* The custom id of the modal.
|
||||
* @type {string}
|
||||
*/
|
||||
this.customId = data.data.custom_id;
|
||||
|
||||
/**
|
||||
* @typedef {Object} PartialTextInputData
|
||||
* @property {string} [customId] A unique string to be sent in the interaction when submitted
|
||||
* @property {MessageComponentType} [type] The type of this component
|
||||
* @property {string} [value] Value of this text input component
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PartialModalActionRow
|
||||
* @property {MessageComponentType} [type] The type of this component
|
||||
* @property {PartialTextInputData[]} [components] Partial text input components
|
||||
*/
|
||||
|
||||
/**
|
||||
* The inputs within the modal
|
||||
* @type {PartialModalActionRow[]}
|
||||
*/
|
||||
this.components =
|
||||
data.data.components?.map(c => ({
|
||||
type: MessageComponentTypes[c.type],
|
||||
components: ModalSubmitInteraction.transformComponent(c),
|
||||
})) ?? [];
|
||||
|
||||
/**
|
||||
* The message associated with this interaction
|
||||
* @type {Message|APIMessage|null}
|
||||
*/
|
||||
this.message = data.message ? this.channel?.messages._add(data.message) ?? data.message : null;
|
||||
|
||||
/**
|
||||
* The fields within the modal
|
||||
* @type {ModalSubmitFieldsResolver}
|
||||
*/
|
||||
this.fields = new ModalSubmitFieldsResolver(this.components);
|
||||
|
||||
/**
|
||||
* Whether the reply to this interaction has been deferred
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.deferred = false;
|
||||
|
||||
/**
|
||||
* Whether the reply to this interaction is ephemeral
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.ephemeral = null;
|
||||
|
||||
/**
|
||||
* Whether this interaction has already been replied to
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.replied = false;
|
||||
|
||||
/**
|
||||
* An associated interaction webhook, can be used to further interact with this interaction
|
||||
* @type {InteractionWebhook}
|
||||
*/
|
||||
this.webhook = new InteractionWebhook(this.client, this.applicationId, this.token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms component data to discord.js-compatible data
|
||||
* @param {*} rawComponent The data to transform
|
||||
* @returns {PartialTextInputData[]}
|
||||
*/
|
||||
static transformComponent(rawComponent) {
|
||||
return rawComponent.components.map(c => ({
|
||||
value: c.value,
|
||||
type: MessageComponentTypes[c.type],
|
||||
customId: c.custom_id,
|
||||
}));
|
||||
}
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by InteractionResponses
|
||||
/* eslint-disable no-empty-function */
|
||||
deferReply() {}
|
||||
reply() {}
|
||||
fetchReply() {}
|
||||
editReply() {}
|
||||
deleteReply() {}
|
||||
followUp() {}
|
||||
update() {}
|
||||
deferUpdate() {}
|
||||
}
|
||||
|
||||
InteractionResponses.applyToClass(ModalSubmitInteraction, ['showModal', 'awaitModalSubmit']);
|
||||
|
||||
module.exports = ModalSubmitInteraction;
|
||||
201
src/structures/TextInputComponent.js
Normal file
201
src/structures/TextInputComponent.js
Normal file
@@ -0,0 +1,201 @@
|
||||
'use strict';
|
||||
|
||||
const BaseMessageComponent = require('./BaseMessageComponent');
|
||||
const { RangeError } = require('../errors');
|
||||
const { TextInputStyles, MessageComponentTypes } = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Represents a text input component in a modal
|
||||
* @extends {BaseMessageComponent}
|
||||
*/
|
||||
|
||||
class TextInputComponent extends BaseMessageComponent {
|
||||
/**
|
||||
* @typedef {BaseMessageComponentOptions} TextInputComponentOptions
|
||||
* @property {string} [customId] A unique string to be sent in the interaction when submitted
|
||||
* @property {string} [label] The text to be displayed above this text input component
|
||||
* @property {number} [maxLength] Maximum length of text that can be entered
|
||||
* @property {number} [minLength] Minimum length of text required to be entered
|
||||
* @property {string} [placeholder] Custom placeholder text to display when no text is entered
|
||||
* @property {boolean} [required] Whether or not this text input component is required
|
||||
* @property {TextInputStyleResolvable} [style] The style of this text input component
|
||||
* @property {string} [value] Value of this text input component
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {TextInputComponent|TextInputComponentOptions} [data={}] TextInputComponent to clone or raw data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
super({ type: 'TEXT_INPUT' });
|
||||
|
||||
this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
/**
|
||||
* A unique string to be sent in the interaction when submitted
|
||||
* @type {?string}
|
||||
*/
|
||||
this.customId = data.custom_id ?? data.customId ?? null;
|
||||
|
||||
/**
|
||||
* The text to be displayed above this text input component
|
||||
* @type {?string}
|
||||
*/
|
||||
this.label = data.label ?? null;
|
||||
|
||||
/**
|
||||
* Maximum length of text that can be entered
|
||||
* @type {?number}
|
||||
*/
|
||||
this.maxLength = data.max_length ?? data.maxLength ?? null;
|
||||
|
||||
/**
|
||||
* Minimum length of text required to be entered
|
||||
* @type {?string}
|
||||
*/
|
||||
this.minLength = data.min_length ?? data.minLength ?? null;
|
||||
|
||||
/**
|
||||
* Custom placeholder text to display when no text is entered
|
||||
* @type {?string}
|
||||
*/
|
||||
this.placeholder = data.placeholder ?? null;
|
||||
|
||||
/**
|
||||
* Whether or not this text input component is required
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.required = data.required ?? false;
|
||||
|
||||
/**
|
||||
* The style of this text input component
|
||||
* @type {?TextInputStyle}
|
||||
*/
|
||||
this.style = data.style ? TextInputComponent.resolveStyle(data.style) : null;
|
||||
|
||||
/**
|
||||
* Value of this text input component
|
||||
* @type {?string}
|
||||
*/
|
||||
this.value = data.value ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom id of this text input component
|
||||
* @param {string} customId A unique string to be sent in the interaction when submitted
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setCustomId(customId) {
|
||||
this.customId = Util.verifyString(customId, RangeError, 'TEXT_INPUT_CUSTOM_ID');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label of this text input component
|
||||
* @param {string} label The text to be displayed above this text input component
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setLabel(label) {
|
||||
this.label = Util.verifyString(label, RangeError, 'TEXT_INPUT_LABEL');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text input component to be required for modal submission
|
||||
* @param {boolean} [required=true] Whether this text input component is required
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setRequired(required = true) {
|
||||
this.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum length of text input required in this text input component
|
||||
* @param {number} maxLength Maximum length of text to be required
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setMaxLength(maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum length of text input required in this text input component
|
||||
* @param {number} minLength Minimum length of text to be required
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setMinLength(minLength) {
|
||||
this.minLength = minLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the placeholder of this text input component
|
||||
* @param {string} placeholder Custom placeholder text to display when no text is entered
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setPlaceholder(placeholder) {
|
||||
this.placeholder = Util.verifyString(placeholder, RangeError, 'TEXT_INPUT_PLACEHOLDER');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the style of this text input component
|
||||
* @param {TextInputStyleResolvable} style The style of this text input component
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setStyle(style) {
|
||||
this.style = TextInputComponent.resolveStyle(style);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this text input component
|
||||
* @param {string} value Value of this text input component
|
||||
* @returns {TextInputComponent}
|
||||
*/
|
||||
setValue(value) {
|
||||
this.value = Util.verifyString(value, RangeError, 'TEXT_INPUT_VALUE');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the text input component into a plain object
|
||||
* @returns {APITextInput} The raw data of this text input component
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
custom_id: this.customId,
|
||||
label: this.label,
|
||||
max_length: this.maxLength,
|
||||
min_length: this.minLength,
|
||||
placeholder: this.placeholder,
|
||||
required: this.required,
|
||||
style: TextInputStyles[this.style],
|
||||
type: MessageComponentTypes[this.type],
|
||||
value: this.value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to a TextInputStyle. This can be
|
||||
* * TextInputStyle
|
||||
* * number
|
||||
* @typedef {number|TextInputStyle} TextInputStyleResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves the style of a text input component
|
||||
* @param {TextInputStyleResolvable} style The style to resolve
|
||||
* @returns {TextInputStyle}
|
||||
* @private
|
||||
*/
|
||||
static resolveStyle(style) {
|
||||
return typeof style === 'string' ? style : TextInputStyles[style];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TextInputComponent;
|
||||
@@ -1,9 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const { Error } = require('../../errors');
|
||||
const { InteractionResponseTypes } = require('../../util/Constants');
|
||||
const { InteractionResponseTypes, InteractionTypes } = require('../../util/Constants');
|
||||
const MessageFlags = require('../../util/MessageFlags');
|
||||
const InteractionCollector = require('../InteractionCollector');
|
||||
const MessagePayload = require('../MessagePayload');
|
||||
const Modal = require('../Modal');
|
||||
|
||||
/**
|
||||
* Interface for classes that support shared interaction response types.
|
||||
@@ -226,6 +228,56 @@ class InteractionResponses {
|
||||
return options.fetchReply ? this.fetchReply() : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a modal component
|
||||
* @param {Modal|ModalOptions} modal The modal to show
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async showModal(modal) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
|
||||
const _modal = modal instanceof Modal ? modal : new Modal(modal);
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.MODAL,
|
||||
data: _modal.toJSON(),
|
||||
},
|
||||
});
|
||||
this.replied = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing the same properties as CollectorOptions, but a few more:
|
||||
* @typedef {Object} AwaitModalSubmitOptions
|
||||
* @property {CollectorFilter} [filter] The filter applied to this collector
|
||||
* @property {number} time Time to wait for an interaction before rejecting
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collects a single modal submit interaction that passes the filter.
|
||||
* The Promise will reject if the time expires.
|
||||
* @param {AwaitModalSubmitOptions} options Options to pass to the internal collector
|
||||
* @returns {Promise<ModalSubmitInteraction>}
|
||||
* @example
|
||||
* // Collect a modal submit interaction
|
||||
* const filter = (interaction) => interaction.customId === 'modal';
|
||||
* interaction.awaitModalSubmit({ filter, time: 15_000 })
|
||||
* .then(interaction => console.log(`${interaction.customId} was submitted!`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
awaitModalSubmit(options) {
|
||||
if (typeof options.time !== 'number') throw new Error('INVALID_TYPE', 'time', 'number');
|
||||
const _options = { ...options, max: 1, interactionType: InteractionTypes.MODAL_SUBMIT };
|
||||
return new Promise((resolve, reject) => {
|
||||
const collector = new InteractionCollector(this.client, _options);
|
||||
collector.once('end', (interactions, reason) => {
|
||||
const interaction = interactions.first();
|
||||
if (interaction) resolve(interaction);
|
||||
else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static applyToClass(structure, ignore = []) {
|
||||
const props = [
|
||||
'deferReply',
|
||||
@@ -236,6 +288,8 @@ class InteractionResponses {
|
||||
'followUp',
|
||||
'deferUpdate',
|
||||
'update',
|
||||
'showModal',
|
||||
'awaitModalSubmit',
|
||||
];
|
||||
|
||||
for (const prop of props) {
|
||||
|
||||
Reference in New Issue
Block a user