defer update if there's no component update

This commit is contained in:
MapleLeaf
2021-12-27 21:41:14 -06:00
parent 3d89b4fe6f
commit 9ab1f2b689
7 changed files with 61 additions and 8 deletions

View File

@@ -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 {

View File

@@ -18,6 +18,7 @@ export type ButtonInteraction = {
channelId: string
customId: string
update(options: MessageOptions): Promise<void>
deferUpdate(): Promise<void>
}
export type SelectInteraction = {
@@ -27,4 +28,5 @@ export type SelectInteraction = {
customId: string
values: string[]
update(options: MessageOptions): Promise<void>
deferUpdate(): Promise<void>
}

View File

@@ -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
}

View File

@@ -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
}
}
}

View File

@@ -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,
<Button label="clic" onClick={() => console.log("was clic")} />,
)
},
},
{
name: "counter",
description: "shows a counter button",

6
test/renderer.test.ts Normal file
View File

@@ -0,0 +1,6 @@
// test that the interaction update is _eventually_ deferred if there's no component update,
// and that update isn't called after the fact
// ...somehow
test.todo("defer update timeout")
export {}

View File

@@ -28,7 +28,7 @@
# internal
- [ ] combine `MessageOptions` and `Message` into a single message object (?)
- [ ] consider always calling `deferUpdate` on component interactions
- [x] consider always calling `deferUpdate` on component interactions
# cool ideas / polish
@@ -45,4 +45,4 @@
- [ ] uncontrolled select
- [x] single class/helper function for testing `ReacordTester`
- [ ] handle deletion outside of reacord
- [ ] for more easily writing adapters, address discord API nuances at the reacord level instead of the adapter level. the goal being that adapters can just take the objects and send them to discord
- [ ] for more easily writing adapters, address discord API nuances at the reacord level instead of the adapter level. the goal being that adapters can just take the objects and send them to discord. probably make use of discord api types for this