reply instance deletion

This commit is contained in:
MapleLeaf
2021-12-27 13:50:56 -06:00
parent 32c1e02f92
commit 8db2a16305
7 changed files with 75 additions and 17 deletions

View File

@@ -100,6 +100,9 @@ function createReacordMessage(message: Discord.Message): Message {
components: message.components, components: message.components,
}) })
}, },
delete: async () => {
await message.delete()
},
} }
} }

View File

@@ -16,6 +16,7 @@ export type ReacordConfig<InteractionInit> = {
export type ReacordInstance = { export type ReacordInstance = {
render: (content: ReactNode) => void render: (content: ReactNode) => void
deactivate: () => void deactivate: () => void
destroy: () => void
} }
export class Reacord<InteractionInit> { export class Reacord<InteractionInit> {
@@ -53,6 +54,10 @@ export class Reacord<InteractionInit> {
deactivate: () => { deactivate: () => {
this.deactivate(renderer) this.deactivate(renderer)
}, },
destroy: () => {
this.renderers = this.renderers.filter((it) => it !== renderer)
renderer.destroy()
},
} }
} }

View File

@@ -45,6 +45,7 @@ export type MessageSelectOptionOptions = {
export type Message = { export type Message = {
edit(options: MessageOptions): Promise<void> edit(options: MessageOptions): Promise<void>
delete(): Promise<void>
disableComponents(): Promise<void> disableComponents(): Promise<void>
} }

View File

@@ -10,10 +10,9 @@ import type { Node } from "./node.js"
// so we know whether to call reply() or followUp() // so we know whether to call reply() or followUp()
const repliedInteractionIds = new Set<string>() const repliedInteractionIds = new Set<string>()
type UpdatePayload = { type UpdatePayload =
options: MessageOptions | { action: "update" | "deactivate"; options: MessageOptions }
action: "update" | "deactivate" | { action: "destroy" }
}
export class Renderer { export class Renderer {
readonly nodes = new Container<Node<unknown>>() readonly nodes = new Container<Node<unknown>>()
@@ -49,6 +48,11 @@ export class Renderer {
}) })
} }
destroy() {
this.active = false
this.updates.next({ action: "destroy" })
}
handleComponentInteraction(interaction: ComponentInteraction) { handleComponentInteraction(interaction: ComponentInteraction) {
this.componentInteraction = interaction this.componentInteraction = interaction
for (const node of this.nodes) { for (const node of this.nodes) {
@@ -70,31 +74,37 @@ export class Renderer {
return options return options
} }
private async updateMessage({ options, action }: UpdatePayload) { private async updateMessage(payload: UpdatePayload) {
if (action === "deactivate" && this.message) { if (payload.action === "destroy") {
this.updateSubscription.unsubscribe() this.updateSubscription.unsubscribe()
await this.message.disableComponents() await this.message?.delete()
return
}
if (payload.action === "deactivate") {
this.updateSubscription.unsubscribe()
await this.message?.disableComponents()
return return
} }
if (this.componentInteraction) { if (this.componentInteraction) {
const promise = this.componentInteraction.update(options) const promise = this.componentInteraction.update(payload.options)
this.componentInteraction = undefined this.componentInteraction = undefined
await promise await promise
return return
} }
if (this.message) { if (this.message) {
await this.message.edit(options) await this.message.edit(payload.options)
return return
} }
if (repliedInteractionIds.has(this.interaction.id)) { if (repliedInteractionIds.has(this.interaction.id)) {
this.message = await this.interaction.followUp(options) this.message = await this.interaction.followUp(payload.options)
return return
} }
repliedInteractionIds.add(this.interaction.id) repliedInteractionIds.add(this.interaction.id)
this.message = await this.interaction.reply(options) this.message = await this.interaction.reply(payload.options)
} }
} }

View File

@@ -17,7 +17,7 @@ import type {
} from "./internal/message" } from "./internal/message"
export class TestAdapter implements Adapter<TestCommandInteraction> { export class TestAdapter implements Adapter<TestCommandInteraction> {
readonly messages: TestMessage[] = [] messages: TestMessage[] = []
private componentInteractionListener: ( private componentInteractionListener: (
interaction: ComponentInteraction, interaction: ComponentInteraction,
@@ -60,6 +60,10 @@ export class TestAdapter implements Adapter<TestCommandInteraction> {
raise(`Couldn't find select with placeholder "${placeholder}"`) raise(`Couldn't find select with placeholder "${placeholder}"`)
} }
removeMessage(message: TestMessage) {
this.messages = this.messages.filter((m) => m !== message)
}
private createButtonActions( private createButtonActions(
button: MessageButtonOptions, button: MessageButtonOptions,
message: TestMessage, message: TestMessage,
@@ -88,7 +92,7 @@ export class TestAdapter implements Adapter<TestCommandInteraction> {
} }
export class TestMessage implements Message { export class TestMessage implements Message {
constructor(public options: MessageOptions) {} constructor(public options: MessageOptions, private adapter: TestAdapter) {}
async edit(options: MessageOptions): Promise<void> { async edit(options: MessageOptions): Promise<void> {
this.options = options this.options = options
@@ -103,6 +107,10 @@ export class TestMessage implements Message {
} }
} }
} }
async delete(): Promise<void> {
this.adapter.removeMessage(this)
}
} }
export class TestCommandInteraction implements CommandInteraction { export class TestCommandInteraction implements CommandInteraction {
@@ -113,7 +121,7 @@ export class TestCommandInteraction implements CommandInteraction {
constructor(private adapter: TestAdapter) {} constructor(private adapter: TestAdapter) {}
private createMesssage(messageOptions: MessageOptions): Message { private createMesssage(messageOptions: MessageOptions): Message {
const message = new TestMessage(messageOptions) const message = new TestMessage(messageOptions, this.adapter)
this.adapter.messages.push(message) this.adapter.messages.push(message)
return message return message
} }

View File

@@ -3,9 +3,9 @@ import { Button, Embed, EmbedField, EmbedTitle } from "../library/main"
import { TestCommandInteraction } from "../library/testing" import { TestCommandInteraction } from "../library/testing"
import { setupReacordTesting } from "./setup-testing" import { setupReacordTesting } from "./setup-testing"
const { reacord, adapter, assertMessages } = setupReacordTesting()
test("rendering behavior", async () => { test("rendering behavior", async () => {
const { reacord, adapter, assertMessages } = setupReacordTesting()
const reply = reacord.createCommandReply(new TestCommandInteraction(adapter)) const reply = reacord.createCommandReply(new TestCommandInteraction(adapter))
reply.render(<KitchenSinkCounter onDeactivate={() => reply.deactivate()} />) reply.render(<KitchenSinkCounter onDeactivate={() => reply.deactivate()} />)
@@ -241,6 +241,32 @@ test("rendering behavior", async () => {
]) ])
}) })
test("delete", async () => {
const { reacord, adapter, assertMessages } = setupReacordTesting()
const reply = reacord.createCommandReply(new TestCommandInteraction(adapter))
reply.render(
<>
some text
<Embed>some embed</Embed>
<Button label="some button" onClick={() => {}} />
</>,
)
await assertMessages([
{
content: "some text",
embeds: [{ description: "some embed" }],
actionRows: [
[{ type: "button", style: "secondary", label: "some button" }],
],
},
])
reply.destroy()
await assertMessages([])
})
// test multiple instances that can be updated independently, // test multiple instances that can be updated independently,
// + old instances getting deactivated once the limit is reached // + old instances getting deactivated once the limit is reached
test.todo("multiple instances") test.todo("multiple instances")

View File

@@ -22,9 +22,14 @@
- [x] action row - [x] action row
- [x] button onClick - [x] button onClick
- [x] deactivate - [x] deactivate
- [ ] destroy - [x] destroy
- [ ] docs - [ ] docs
# internal
- [ ] combine `MessageOptions` and `Message` into a single message object (?)
- [ ] consider always calling `deferUpdate` on component interactions
# cool ideas / polish # cool ideas / polish
- [ ] message property on reacord instance - [ ] message property on reacord instance