diff --git a/library/core/reacord-discord-js.ts b/library/core/reacord-discord-js.ts index dc90959..31b32ae 100644 --- a/library/core/reacord-discord-js.ts +++ b/library/core/reacord-discord-js.ts @@ -84,6 +84,7 @@ function createReacordComponentInteraction( update: async (options) => { await interaction.update(getDiscordMessageOptions(options)) }, + deferUpdate: () => interaction.deferUpdate(), } } @@ -97,6 +98,7 @@ function createReacordComponentInteraction( update: async (options) => { await interaction.update(getDiscordMessageOptions(options)) }, + deferUpdate: () => interaction.deferUpdate(), } } @@ -106,13 +108,13 @@ function createReacordComponentInteraction( // TODO: this could be a part of the core library, // and also handle some edge cases, e.g. empty messages function getDiscordMessageOptions( - options: MessageOptions, + reacordOptions: MessageOptions, ): Discord.MessageOptions { - return { + const options: Discord.MessageOptions = { // eslint-disable-next-line unicorn/no-null - content: options.content || null, - embeds: options.embeds, - components: options.actionRows.map((row) => ({ + content: reacordOptions.content || null, + embeds: reacordOptions.embeds, + components: reacordOptions.actionRows.map((row) => ({ type: "ACTION_ROW", components: row.map( (component): Discord.MessageActionRowComponentOptions => { @@ -143,6 +145,12 @@ function getDiscordMessageOptions( ), })), } + + if (!options.content && !options.embeds?.length) { + options.content = "_ _" + } + + return options } function createReacordMessage(message: Discord.Message): Message { diff --git a/library/internal/interaction.ts b/library/internal/interaction.ts index e5d9bba..e686e99 100644 --- a/library/internal/interaction.ts +++ b/library/internal/interaction.ts @@ -18,6 +18,7 @@ export type ButtonInteraction = { channelId: string customId: string update(options: MessageOptions): Promise + deferUpdate(): Promise } export type SelectInteraction = { @@ -27,4 +28,5 @@ export type SelectInteraction = { customId: string values: string[] update(options: MessageOptions): Promise + deferUpdate(): Promise } diff --git a/library/internal/renderer.ts b/library/internal/renderer.ts index 61177f6..fdbe03a 100644 --- a/library/internal/renderer.ts +++ b/library/internal/renderer.ts @@ -4,6 +4,7 @@ import { Container } from "./container.js" import type { ComponentInteraction } from "./interaction" import type { Message, MessageOptions } from "./message" import type { Node } from "./node.js" +import { Timeout } from "./timeout" type UpdatePayload = | { action: "update" | "deactivate"; options: MessageOptions } @@ -20,6 +21,10 @@ export abstract class Renderer { .pipe(concatMap((payload) => this.updateMessage(payload))) .subscribe({ error: console.error }) + private deferUpdateTimeout = new Timeout(500, () => { + this.componentInteraction?.deferUpdate().catch(console.error) + }) + render() { if (!this.active) { console.warn("Attempted to update a deactivated message") @@ -47,6 +52,7 @@ export abstract class Renderer { handleComponentInteraction(interaction: ComponentInteraction) { this.componentInteraction = interaction + this.deferUpdateTimeout.run() for (const node of this.nodes) { if (node.handleComponentInteraction(interaction)) { return true @@ -84,6 +90,7 @@ export abstract class Renderer { if (this.componentInteraction) { const promise = this.componentInteraction.update(payload.options) this.componentInteraction = undefined + this.deferUpdateTimeout.cancel() await promise return } diff --git a/library/internal/timeout.ts b/library/internal/timeout.ts new file mode 100644 index 0000000..c3b178c --- /dev/null +++ b/library/internal/timeout.ts @@ -0,0 +1,20 @@ +export class Timeout { + private timeoutId?: NodeJS.Timeout + + constructor( + private readonly time: number, + private readonly callback: () => void, + ) {} + + run() { + this.cancel() + this.timeoutId = setTimeout(this.callback, this.time) + } + + cancel() { + if (this.timeoutId) { + clearTimeout(this.timeoutId) + this.timeoutId = undefined + } + } +} diff --git a/playground/main.tsx b/playground/main.tsx index f50dcf9..d41c45a 100644 --- a/playground/main.tsx +++ b/playground/main.tsx @@ -1,7 +1,7 @@ import { Client } from "discord.js" import "dotenv/config" import React from "react" -import { ReacordDiscordJs } from "../library/main" +import { Button, ReacordDiscordJs } from "../library/main" import { createCommandHandler } from "./command-handler" import { Counter } from "./counter" import { FruitSelect } from "./fruit-select" @@ -36,6 +36,16 @@ client.on("ready", () => { }) createCommandHandler(client, [ + { + name: "button", + description: "it's a button", + run: (interaction) => { + reacord.reply( + interaction, +