destroying messages, placeholder for deactivate

This commit is contained in:
itsMapleLeaf
2022-07-23 19:19:13 -05:00
parent 4db32ddbbb
commit 05c940ff52
3 changed files with 120 additions and 25 deletions

View File

@@ -18,13 +18,18 @@ export function createReacordDiscordJs(
const manager = createReacordInstanceManager(options) const manager = createReacordInstanceManager(options)
return { return {
send(channelId: string, initialContent?: ReactNode) { send(channelId: string, initialContent?: ReactNode) {
const messageUpdater = createMessageUpdater() const handler = createMessageHandler()
return manager.createInstance(initialContent, async (tree) => { return manager.createInstance({
initialContent,
update: async (tree) => {
const messageOptions: MessageOptions & MessageEditOptions = { const messageOptions: MessageOptions & MessageEditOptions = {
content: tree.children.map((child) => child.text).join(""), content: tree.children.map((child) => child.text).join(""),
} }
const channel = await getTextChannel(client, channelId) const channel = await getTextChannel(client, channelId)
await messageUpdater.update(messageOptions, channel) await handler.update(messageOptions, channel)
},
destroy: () => handler.destroy(),
deactivate: () => handler.deactivate(),
}) })
}, },
@@ -34,8 +39,9 @@ export function createReacordDiscordJs(
} }
} }
function createMessageUpdater() { function createMessageHandler() {
let message: Message | undefined let message: Message | undefined
let active = true
const queue = createAsyncQueue() const queue = createAsyncQueue()
async function update( async function update(
@@ -43,6 +49,7 @@ function createMessageUpdater() {
channel: TextBasedChannel, channel: TextBasedChannel,
) { ) {
return queue.add(async () => { return queue.add(async () => {
if (!active) return
if (message) { if (message) {
await message.edit(options) await message.edit(options)
} else { } else {
@@ -51,10 +58,27 @@ function createMessageUpdater() {
}) })
} }
return { update } async function destroy() {
return queue.add(async () => {
active = false
await message?.delete()
})
}
async function deactivate() {
return queue.add(async () => {
active = false
// TODO: disable message components
})
}
return { update, destroy, deactivate }
} }
async function getTextChannel(client: Client<boolean>, channelId: string) { async function getTextChannel(
client: Client<boolean>,
channelId: string,
): Promise<TextBasedChannel> {
let channel = client.channels.cache.get(channelId) let channel = client.channels.cache.get(channelId)
if (!channel) { if (!channel) {
channel = (await client.channels.fetch(channelId)) ?? undefined channel = (await client.channels.fetch(channelId)) ?? undefined

View File

@@ -24,13 +24,31 @@ export type ReacordInstance = {
deactivate: () => void deactivate: () => void
} }
type ReacordInstanceOptions = {
initialContent: ReactNode
update: (tree: MessageTree) => unknown
deactivate: () => unknown
destroy: () => unknown
}
export function createReacordInstanceManager({ export function createReacordInstanceManager({
maxInstances = 50, maxInstances = 50,
}: ReacordOptions) { }: ReacordOptions) {
const instances: ReacordInstance[] = [] const instances: ReacordInstance[] = []
function createInstance(...args: Parameters<typeof createReacordInstance>) { function createInstance(options: ReacordInstanceOptions) {
const instance = createReacordInstance(...args) const instance = createReacordInstance({
...options,
deactivate() {
instances.splice(instances.indexOf(instance), 1)
return options.deactivate()
},
destroy() {
instances.splice(instances.indexOf(instance), 1)
return options.destroy()
},
})
instances.push(instance) instances.push(instance)
if (instances.length > maxInstances) { if (instances.length > maxInstances) {
@@ -44,14 +62,13 @@ export function createReacordInstanceManager({
} }
function createReacordInstance( function createReacordInstance(
initialContent: ReactNode, options: ReacordInstanceOptions,
render: (tree: MessageTree) => unknown,
): ReacordInstance { ): ReacordInstance {
const tree: MessageTree = { const tree: MessageTree = {
children: [], children: [],
render: async () => { render: async () => {
try { try {
await render(tree) await options.update(tree)
} catch (error) { } catch (error) {
console.error( console.error(
"Reacord encountered an error while updating the message.", "Reacord encountered an error while updating the message.",
@@ -79,12 +96,30 @@ function createReacordInstance(
render(content: ReactNode) { render(content: ReactNode) {
reconciler.updateContainer(content, container) reconciler.updateContainer(content, container)
}, },
destroy() {}, async deactivate() {
deactivate() {}, try {
await options.deactivate()
} catch (error) {
console.error(
"Reacord encountered an error while deactivating an instance.",
error,
)
}
},
async destroy() {
try {
await options.destroy()
} catch (error) {
console.error(
"Reacord encountered an error while destroying an instance.",
error,
)
}
},
} }
if (initialContent !== undefined) { if (options.initialContent !== undefined) {
instance.render(initialContent) instance.render(options.initialContent)
} }
return instance return instance

View File

@@ -3,6 +3,8 @@ import { ChannelType, Client, IntentsBitField } from "discord.js"
import "dotenv/config" import "dotenv/config"
import { kebabCase } from "lodash-es" import { kebabCase } from "lodash-es"
import React, { useEffect, useState } from "react" import React, { useEffect, useState } from "react"
import { raise } from "../helpers/raise"
import { waitFor } from "../helpers/wait-for"
import { createReacordDiscordJs } from "../library.new/discord-js" import { createReacordDiscordJs } from "../library.new/discord-js"
const client = new Client({ intents: IntentsBitField.Flags.Guilds }) const client = new Client({ intents: IntentsBitField.Flags.Guilds })
@@ -26,6 +28,7 @@ for (const [, channel] of category.children.cache) {
let prefix = 0 let prefix = 0
const createTest = async ( const createTest = async (
name: string, name: string,
description: string,
block: (channel: TextChannel) => void | Promise<unknown>, block: (channel: TextChannel) => void | Promise<unknown>,
) => { ) => {
prefix += 1 prefix += 1
@@ -33,10 +36,11 @@ const createTest = async (
type: ChannelType.GuildText, type: ChannelType.GuildText,
name: `${String(prefix).padStart(3, "0")}-${kebabCase(name)}`, name: `${String(prefix).padStart(3, "0")}-${kebabCase(name)}`,
}) })
await channel.edit({ topic: description })
await block(channel) await block(channel)
} }
await createTest("basic", (channel) => { await createTest("basic", "should update over time", (channel) => {
function Timer() { function Timer() {
const [count, setCount] = useState(0) const [count, setCount] = useState(0)
@@ -53,8 +57,40 @@ await createTest("basic", (channel) => {
reacord.send(channel.id, <Timer />) reacord.send(channel.id, <Timer />)
}) })
await createTest("immediate renders", async (channel) => { await createTest(
"immediate renders",
`should process renders in sequence; this should show "hi moon"`,
async (channel) => {
const instance = reacord.send(channel.id) const instance = reacord.send(channel.id)
instance.render("hi world") instance.render("hi world")
instance.render("hi moon") instance.render("hi moon")
}) },
)
await createTest(
"destroy",
"should remove the message; this channel should be empty",
async (channel) => {
const instance = reacord.send(channel.id)
instance.render("hi world")
instance.render("hi moon")
await waitFor(async () => {
const messages = await channel.messages.fetch({ limit: 1 })
if (messages.first()?.content !== "hi moon") {
raise("not ready")
}
})
instance.destroy()
},
)
await createTest(
"immediate destroy",
"should never show if called immediately; this channel should be empty",
async (channel) => {
const instance = reacord.send(channel.id)
instance.render("hi world")
instance.render("hi moon")
instance.destroy()
},
)