feat: enhance modal handling with options for wait time and improved promise resolution

This commit is contained in:
MARCROCK22 2025-06-19 12:14:01 -04:00
parent cccf1ea2c4
commit 0f39bae759
7 changed files with 75 additions and 37 deletions

View File

@ -1,5 +1,4 @@
import type { RestOrArray } from '../common'; import type { RestOrArray } from '../common';
import { ModalSubmitInteraction } from '../structures';
import { import {
type APIActionRowComponent, type APIActionRowComponent,
type APIModalInteractionResponseCallbackData, type APIModalInteractionResponseCallbackData,
@ -90,19 +89,6 @@ export class Modal<T extends ModalBuilderComponents = TextInput> {
return this; return this;
} }
waitFor(timeout?: number): Promise<ModalSubmitInteraction<boolean> | null> {
return new Promise<ModalSubmitInteraction<boolean> | null>(res => {
this.run(interaction => {
res(interaction);
});
if (timeout && timeout > 0) {
setTimeout(() => {
res(null);
}, timeout);
}
});
}
/** /**
* Converts the modal to JSON format. * Converts the modal to JSON format.
* @returns The modal data in JSON format. * @returns The modal data in JSON format.

View File

@ -12,10 +12,11 @@ import type {
MakeRequired, MakeRequired,
MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple, UnionToTuple,
When, When,
} from '../../common'; } from '../../common';
import type { AllChannels, EntryPointInteraction } from '../../structures'; import type { AllChannels, EntryPointInteraction, ModalSubmitInteraction } from '../../structures';
import { MessageFlags, type RESTGetAPIGuildQuery } from '../../types'; import { MessageFlags, type RESTGetAPIGuildQuery } from '../../types';
import { BaseContext } from '../basecontext'; import { BaseContext } from '../basecontext';
import type { RegisteredMiddlewares } from '../decorators'; import type { RegisteredMiddlewares } from '../decorators';
@ -52,8 +53,11 @@ export class EntryPointContext<M extends keyof RegisteredMiddlewares = never> ex
return this.interaction.write<WR>(body, withResponse); return this.interaction.write<WR>(body, withResponse);
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options === undefined) return this.interaction.modal(body);
return this.interaction.modal(body, options);
} }
deferReply<WR extends boolean = false>( deferReply<WR extends boolean = false>(

View File

@ -8,16 +8,22 @@ import {
type WebhookMessageStructure, type WebhookMessageStructure,
} from '../../client/transformers'; } from '../../client/transformers';
import { import {
type InteractionCreateBodyRequest, InteractionCreateBodyRequest,
type InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
type MakeRequired, MakeRequired,
type MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
toSnakeCase, toSnakeCase,
type UnionToTuple, UnionToTuple,
type When, When,
} from '../../common'; } from '../../common';
import type { AllChannels, MessageCommandInteraction, UserCommandInteraction } from '../../structures'; import {
AllChannels,
MessageCommandInteraction,
ModalSubmitInteraction,
UserCommandInteraction,
} from '../../structures';
import { type APIMessage, ApplicationCommandType, MessageFlags, type RESTGetAPIGuildQuery } from '../../types'; import { type APIMessage, ApplicationCommandType, MessageFlags, type RESTGetAPIGuildQuery } from '../../types';
import { BaseContext } from '../basecontext'; import { BaseContext } from '../basecontext';
import type { RegisteredMiddlewares } from '../decorators'; import type { RegisteredMiddlewares } from '../decorators';
@ -75,8 +81,11 @@ export class MenuCommandContext<
return this.interaction.write<WR>(body, withResponse); return this.interaction.write<WR>(body, withResponse);
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options === undefined) return this.interaction.modal(body);
return this.interaction.modal(body, options);
} }
deferReply<WR extends boolean = false>( deferReply<WR extends boolean = false>(

View File

@ -70,3 +70,7 @@ export type InteractionCreateBodyRequest = OmitInsert<
>; >;
export type ModalCreateBodyRequest = APIModalInteractionResponse['data'] | Modal; export type ModalCreateBodyRequest = APIModalInteractionResponse['data'] | Modal;
export interface ModalCreateOptions {
waitFor?: number;
}

View File

@ -18,16 +18,18 @@ import type {
} from '../client/transformers'; } from '../client/transformers';
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands'; import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
import { BaseContext } from '../commands/basecontext'; import { BaseContext } from '../commands/basecontext';
import type { import {
ComponentInteractionMessageUpdate, ComponentInteractionMessageUpdate,
InteractionCreateBodyRequest, InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
MakeRequired, MakeRequired,
MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple, UnionToTuple,
When, When,
} from '../common'; } from '../common';
import { ModalSubmitInteraction } from '../structures';
import { ComponentType, MessageFlags, type RESTGetAPIGuildQuery } from '../types'; import { ComponentType, MessageFlags, type RESTGetAPIGuildQuery } from '../types';
export interface ComponentContext< export interface ComponentContext<
@ -150,8 +152,11 @@ export class ComponentContext<
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options === undefined) return this.interaction.modal(body);
return this.interaction.modal(body, options);
} }
/** /**

View File

@ -1,4 +1,4 @@
import type { AllChannels, Interaction, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..'; import type { AllChannels, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..';
import type { import type {
GuildMemberStructure, GuildMemberStructure,
GuildStructure, GuildStructure,
@ -8,12 +8,13 @@ import type {
} from '../client/transformers'; } from '../client/transformers';
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands'; import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
import { BaseContext } from '../commands/basecontext'; import { BaseContext } from '../commands/basecontext';
import type { import {
InteractionCreateBodyRequest, InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
MakeRequired, MakeRequired,
MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple, UnionToTuple,
When, When,
} from '../common'; } from '../common';
@ -119,9 +120,11 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest): ReturnType<Interaction['modal']> { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
//@ts-expect-error modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
// @ts-expect-error
return this.interaction.modal(body, options);
} }
/** /**

View File

@ -21,6 +21,7 @@ import {
type MessageUpdateBodyRequest, type MessageUpdateBodyRequest,
type MessageWebhookCreateBodyRequest, type MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest, type ModalCreateBodyRequest,
ModalCreateOptions,
type ObjectToLower, type ObjectToLower,
type OmitInsert, type OmitInsert,
type ToClass, type ToClass,
@ -472,11 +473,37 @@ export class Interaction<
) as never; ) as never;
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
async modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options !== undefined && !(body instanceof Modal)) {
body = new Modal(body);
}
if (options === undefined)
return this.reply({ return this.reply({
type: InteractionResponseType.Modal, type: InteractionResponseType.Modal,
data: body, data: body,
}); });
const promise = new Promise<ModalSubmitInteraction | null>(res => {
let nodeTimeout: NodeJS.Timeout | undefined;
// body is always a modal here, so we can safely cast it
(body as Modal).__exec = (interaction: ModalSubmitInteraction) => {
res(interaction);
clearTimeout(nodeTimeout);
};
if (options?.waitFor && options?.waitFor > 0) {
nodeTimeout = setTimeout(() => {
res(null);
}, options.waitFor);
}
});
await this.reply({
type: InteractionResponseType.Modal,
data: body,
});
return promise;
} }
async editOrReply<FR extends boolean = false>( async editOrReply<FR extends boolean = false>(