destroying messages, placeholder for deactivate
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user