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 { ModalSubmitInteraction } from '../structures';
import {
type APIActionRowComponent,
type APIModalInteractionResponseCallbackData,
@ -90,19 +89,6 @@ export class Modal<T extends ModalBuilderComponents = TextInput> {
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.
* @returns The modal data in JSON format.

View File

@ -12,10 +12,11 @@ import type {
MakeRequired,
MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple,
When,
} from '../../common';
import type { AllChannels, EntryPointInteraction } from '../../structures';
import type { AllChannels, EntryPointInteraction, ModalSubmitInteraction } from '../../structures';
import { MessageFlags, type RESTGetAPIGuildQuery } from '../../types';
import { BaseContext } from '../basecontext';
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);
}
modal(body: ModalCreateBodyRequest) {
return this.interaction.modal(body);
modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
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>(

View File

@ -8,16 +8,22 @@ import {
type WebhookMessageStructure,
} from '../../client/transformers';
import {
type InteractionCreateBodyRequest,
type InteractionMessageUpdateBodyRequest,
type MakeRequired,
type MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest,
InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest,
MakeRequired,
MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest,
ModalCreateOptions,
toSnakeCase,
type UnionToTuple,
type When,
UnionToTuple,
When,
} 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 { BaseContext } from '../basecontext';
import type { RegisteredMiddlewares } from '../decorators';
@ -75,8 +81,11 @@ export class MenuCommandContext<
return this.interaction.write<WR>(body, withResponse);
}
modal(body: ModalCreateBodyRequest) {
return this.interaction.modal(body);
modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
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>(

View File

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

View File

@ -18,16 +18,18 @@ import type {
} from '../client/transformers';
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
import { BaseContext } from '../commands/basecontext';
import type {
import {
ComponentInteractionMessageUpdate,
InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest,
MakeRequired,
MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple,
When,
} from '../common';
import { ModalSubmitInteraction } from '../structures';
import { ComponentType, MessageFlags, type RESTGetAPIGuildQuery } from '../types';
export interface ComponentContext<
@ -150,8 +152,11 @@ export class ComponentContext<
return this.interaction.deleteResponse();
}
modal(body: ModalCreateBodyRequest) {
return this.interaction.modal(body);
modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
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 {
GuildMemberStructure,
GuildStructure,
@ -8,12 +8,13 @@ import type {
} from '../client/transformers';
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
import { BaseContext } from '../commands/basecontext';
import type {
import {
InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest,
MakeRequired,
MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple,
When,
} from '../common';
@ -119,9 +120,11 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
return this.interaction.deleteResponse();
}
modal(body: ModalCreateBodyRequest): ReturnType<Interaction['modal']> {
//@ts-expect-error
return this.interaction.modal(body);
modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
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 MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest,
ModalCreateOptions,
type ObjectToLower,
type OmitInsert,
type ToClass,
@ -472,11 +473,37 @@ export class Interaction<
) as never;
}
modal(body: ModalCreateBodyRequest) {
return this.reply({
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({
type: InteractionResponseType.Modal,
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>(