button onClick

This commit is contained in:
MapleLeaf
2021-12-22 12:57:15 -06:00
parent 067b9b43a1
commit b3b0fdc279
4 changed files with 133 additions and 26 deletions

View File

@@ -1,4 +1,9 @@
import type { EmojiResolvable, MessageButtonStyle } from "discord.js"
import type {
ButtonInteraction,
EmojiResolvable,
MessageButtonStyle,
} from "discord.js"
import { nanoid } from "nanoid"
import React from "react"
export type ButtonStyle = Exclude<Lowercase<MessageButtonStyle>, "link">
@@ -7,13 +12,19 @@ export type ButtonProps = {
style?: ButtonStyle
emoji?: EmojiResolvable
disabled?: boolean
onClick: (interaction: ButtonInteraction) => void
children?: React.ReactNode
}
export function Button(props: ButtonProps) {
return (
<reacord-element
createNode={() => ({ ...props, type: "button", children: [] })}
createNode={() => ({
...props,
type: "button",
children: [],
customId: nanoid(),
})}
>
{props.children}
</reacord-element>

View File

@@ -1,12 +1,12 @@
import type {
BaseMessageComponentOptions,
ButtonInteraction,
ColorResolvable,
EmojiResolvable,
MessageActionRowOptions,
MessageEmbedOptions,
MessageOptions,
} from "discord.js"
import { nanoid } from "nanoid"
import type { ButtonStyle } from "./components/button.js"
import { last } from "./helpers/last.js"
import { toUpper } from "./helpers/to-upper.js"
@@ -63,6 +63,8 @@ type ButtonNode = {
style?: ButtonStyle
emoji?: EmojiResolvable
disabled?: boolean
customId: string
onClick: (interaction: ButtonInteraction) => void
children: Node[]
}
@@ -181,12 +183,30 @@ function addActionRowItems(components: ActionRowOptions[], nodes: Node[]) {
if (node.type === "button") {
actionRow.components.push({
type: "BUTTON",
label: node.children.map(getNodeText).join(""),
label: node.children.map(getNodeText).join("") || "_ _",
style: node.style ? toUpper(node.style) : "SECONDARY",
emoji: node.emoji,
disabled: node.disabled,
customId: nanoid(),
customId: node.customId,
})
}
}
}
type InteractionHandler = {
type: "button"
customId: string
onClick: (interaction: ButtonInteraction) => void
}
export function collectInteractionHandlers(node: Node): InteractionHandler[] {
if (node.type === "button") {
return [{ type: "button", customId: node.customId, onClick: node.onClick }]
}
if ("children" in node) {
return node.children.flatMap(collectInteractionHandlers)
}
return []
}

View File

@@ -1,31 +1,65 @@
import type { Message, MessageOptions, TextBasedChannels } from "discord.js"
import type {
InteractionCollector,
Message,
MessageComponentInteraction,
MessageComponentType,
TextBasedChannels,
} from "discord.js"
import type { MessageNode } from "./node-tree.js"
import { getMessageOptions } from "./node-tree.js"
import { collectInteractionHandlers, getMessageOptions } from "./node-tree.js"
type Action =
| { type: "updateMessage"; options: MessageOptions }
| { type: "updateMessage"; tree: MessageNode }
| { type: "deleteMessage" }
export class MessageRenderer {
private channel: TextBasedChannels
private interactionCollector: InteractionCollector<MessageComponentInteraction>
private message?: Message
private tree?: MessageNode
private actions: Action[] = []
private runningPromise?: Promise<void>
constructor(channel: TextBasedChannels) {
this.channel = channel
this.interactionCollector =
this.createInteractionCollector() as InteractionCollector<MessageComponentInteraction>
}
private getInteractionHandler(customId: string) {
if (!this.tree) return undefined
const handlers = collectInteractionHandlers(this.tree)
return handlers.find((handler) => handler.customId === customId)
}
private createInteractionCollector() {
const collector =
this.channel.createMessageComponentCollector<MessageComponentType>({
filter: (interaction) =>
!!this.getInteractionHandler(interaction.customId),
})
collector.on("collect", (interaction) => {
const handler = this.getInteractionHandler(interaction.customId)
if (handler?.type === "button" && interaction.isButton()) {
handler.onClick(interaction)
}
})
return collector
}
render(node: MessageNode) {
this.addAction({
type: "updateMessage",
options: getMessageOptions(node),
tree: node,
})
}
destroy() {
this.actions = []
this.addAction({ type: "deleteMessage" })
this.interactionCollector.stop()
}
completion() {
@@ -65,15 +99,20 @@ export class MessageRenderer {
private async runAction(action: Action) {
if (action.type === "updateMessage") {
this.message = await (this.message
? this.message.edit({
...action.options,
const options = getMessageOptions(action.tree)
// eslint-disable-next-line unicorn/prefer-ternary
if (this.message) {
this.message = await this.message.edit({
...options,
// need to ensure that, if there's no text, it's erased
// eslint-disable-next-line unicorn/no-null
content: action.options.content ?? null,
})
: this.channel.send(action.options))
// need to ensure that, if there's no text, it's erased
// eslint-disable-next-line unicorn/no-null
content: options.content ?? null,
})
} else {
this.message = await this.channel.send(options)
}
this.tree = action.tree
}
if (action.type === "deleteMessage") {