From f9564897aaec2e466ed8ae961223c57edd8619b4 Mon Sep 17 00:00:00 2001 From: itsMapleLeaf <19603573+itsMapleLeaf@users.noreply.github.com> Date: Sun, 24 Jul 2022 13:39:55 -0500 Subject: [PATCH] classes are fine, actually! + simplified things more --- packages/reacord/library.new/async-queue.ts | 24 ++-- packages/reacord/library.new/discord-js.ts | 109 +++++++------- packages/reacord/library.new/main.ts | 2 +- packages/reacord/library.new/reacord.ts | 148 ++++++++------------ 4 files changed, 135 insertions(+), 148 deletions(-) diff --git a/packages/reacord/library.new/async-queue.ts b/packages/reacord/library.new/async-queue.ts index 3c3b96f..5837b78 100644 --- a/packages/reacord/library.new/async-queue.ts +++ b/packages/reacord/library.new/async-queue.ts @@ -1,27 +1,25 @@ export type AsyncCallback = () => unknown -export function createAsyncQueue() { - const callbacks: AsyncCallback[] = [] - let promise: Promise | undefined +export class AsyncQueue { + private callbacks: AsyncCallback[] = [] + private promise: Promise | undefined - async function add(callback: AsyncCallback) { - callbacks.push(callback) - if (promise) return promise + async add(callback: AsyncCallback) { + this.callbacks.push(callback) + if (this.promise) return this.promise - promise = runQueue() + this.promise = this.runQueue() try { - await promise + await this.promise } finally { - promise = undefined + this.promise = undefined } } - async function runQueue() { + private async runQueue() { let callback: AsyncCallback | undefined - while ((callback = callbacks.shift())) { + while ((callback = this.callbacks.shift())) { await callback() } } - - return { add } } diff --git a/packages/reacord/library.new/discord-js.ts b/packages/reacord/library.new/discord-js.ts index 084bfa0..614167f 100644 --- a/packages/reacord/library.new/discord-js.ts +++ b/packages/reacord/library.new/discord-js.ts @@ -7,82 +7,97 @@ import type { TextBasedChannel, } from "discord.js" import type { ReactNode } from "react" -import { createAsyncQueue } from "./async-queue" +import { AsyncQueue } from "./async-queue" import type { ReacordOptions } from "./reacord" -import { createReacordInstanceManager } from "./reacord" +import { ReacordInstancePool } from "./reacord" -export function createReacordDiscordJs( - client: Client, - options: ReacordOptions = {}, -) { - const manager = createReacordInstanceManager(options) - return { - send(channelId: string, initialContent?: ReactNode) { - const handler = createMessageHandler() - return manager.createInstance({ - initialContent, - update: async (tree) => { +export class ReacordDiscordJs { + private instances + + constructor(private readonly client: Client, options: ReacordOptions = {}) { + this.instances = new ReacordInstancePool(options) + } + + send(channelId: string, initialContent?: ReactNode) { + const renderer = new MessageRenderer() + + return this.instances.create({ + initialContent, + update: async (tree) => { + try { const messageOptions: MessageOptions & MessageEditOptions = { content: tree.children.map((child) => child.text).join(""), } - const channel = await getTextChannel(client, channelId) - await handler.update(messageOptions, channel) - }, - destroy: () => handler.destroy(), - deactivate: () => handler.deactivate(), - }) - }, - - reply(interaction: Interaction, initialContent?: ReactNode) {}, - - ephemeralReply(interaction: Interaction, initialContent?: ReactNode) {}, + const channel = await getTextChannel(this.client, channelId) + await renderer.update(messageOptions, channel) + } catch (error) { + console.error("Error updating message:", error) + } + }, + destroy: async () => { + try { + await renderer.destroy() + } catch (error) { + console.error("Error destroying message:", error) + } + }, + deactivate: async () => { + try { + await renderer.deactivate() + } catch (error) { + console.error("Error deactivating message:", error) + } + }, + }) } + + reply(interaction: Interaction, initialContent?: ReactNode) {} + + ephemeralReply(interaction: Interaction, initialContent?: ReactNode) {} } -function createMessageHandler() { - let message: Message | undefined - let active = true - const queue = createAsyncQueue() +class MessageRenderer { + private message: Message | undefined + private active = true + private readonly queue = new AsyncQueue() - async function update( + update( options: MessageOptions & MessageEditOptions, channel: TextBasedChannel, ) { - return queue.add(async () => { - if (!active) return - if (message) { - await message.edit(options) + return this.queue.add(async () => { + if (!this.active) return + if (this.message) { + await this.message.edit(options) } else { - message = await channel.send(options) + this.message = await channel.send(options) } }) } - async function destroy() { - return queue.add(async () => { - active = false - await message?.delete() + destroy() { + return this.queue.add(async () => { + this.active = false + await this.message?.delete() }) } - async function deactivate() { - return queue.add(async () => { - active = false + deactivate() { + return this.queue.add(async () => { + this.active = false // TODO: disable message components }) } - - return { update, destroy, deactivate } } async function getTextChannel( client: Client, channelId: string, ): Promise { - let channel = client.channels.cache.get(channelId) - if (!channel) { - channel = (await client.channels.fetch(channelId)) ?? undefined - } + const channel = + client.channels.cache.get(channelId) ?? + (await client.channels.fetch(channelId)) + if (!channel) { throw new Error(`Channel ${channelId} not found`) } diff --git a/packages/reacord/library.new/main.ts b/packages/reacord/library.new/main.ts index 7043377..b7874cc 100644 --- a/packages/reacord/library.new/main.ts +++ b/packages/reacord/library.new/main.ts @@ -1,2 +1,2 @@ -export { createReacordDiscordJs } from "./discord-js" +export { ReacordDiscordJs } from "./discord-js" export { type ReacordInstance, type ReacordOptions } from "./reacord" diff --git a/packages/reacord/library.new/reacord.ts b/packages/reacord/library.new/reacord.ts index 63a6ac7..c30485b 100644 --- a/packages/reacord/library.new/reacord.ts +++ b/packages/reacord/library.new/reacord.ts @@ -24,103 +24,77 @@ export type ReacordInstance = { deactivate: () => void } -type ReacordInstanceOptions = { +export type ReacordInstanceOptions = { initialContent: ReactNode - update: (tree: MessageTree) => unknown - deactivate: () => unknown - destroy: () => unknown + update: (tree: MessageTree) => Promise + deactivate: () => Promise + destroy: () => Promise } -export function createReacordInstanceManager({ - maxInstances = 50, -}: ReacordOptions) { - const instances: ReacordInstance[] = [] +export class ReacordInstancePool { + private readonly options: Required + private readonly instances = new Set() - function createInstance(options: ReacordInstanceOptions) { - const instance = createReacordInstance({ - ...options, - deactivate() { - instances.splice(instances.indexOf(instance), 1) - return options.deactivate() + constructor({ maxInstances = 50 }: ReacordOptions) { + this.options = { maxInstances } + } + + create(options: ReacordInstanceOptions) { + const tree: MessageTree = { + children: [], + render: async () => { + try { + await options.update(tree) + } catch (error) { + console.error("Failed to update message.", error) + } }, - destroy() { - instances.splice(instances.indexOf(instance), 1) - return options.destroy() + } + + const container = reconciler.createContainer( + tree, + 0, + // eslint-disable-next-line unicorn/no-null + null, + false, + // eslint-disable-next-line unicorn/no-null + null, + "reacord", + () => {}, + // eslint-disable-next-line unicorn/no-null + null, + ) + + const instance: ReacordInstance = { + render: (content: ReactNode) => { + reconciler.updateContainer(content, container) }, - }) + deactivate: async () => { + this.instances.delete(instance) + try { + await options.deactivate() + } catch (error) { + console.error("Failed to deactivate message.", error) + } + }, + destroy: async () => { + this.instances.delete(instance) + try { + await options.destroy() + } catch (error) { + console.error("Failed to destroy message.", error) + } + }, + } - instances.push(instance) + if (options.initialContent !== undefined) { + instance.render(options.initialContent) + } - if (instances.length > maxInstances) { - instances.shift()?.deactivate() + if (this.instances.size > this.options.maxInstances) { + ;[...this.instances][0]?.deactivate() } return instance } - - return { createInstance } -} - -function createReacordInstance( - options: ReacordInstanceOptions, -): ReacordInstance { - const tree: MessageTree = { - children: [], - render: async () => { - try { - await options.update(tree) - } catch (error) { - console.error( - "Reacord encountered an error while updating the message.", - error, - ) - } - }, - } - - const container = reconciler.createContainer( - tree, - 0, - // eslint-disable-next-line unicorn/no-null - null, - false, - // eslint-disable-next-line unicorn/no-null - null, - "reacord", - () => {}, - // eslint-disable-next-line unicorn/no-null - null, - ) - - const instance: ReacordInstance = { - render(content: ReactNode) { - reconciler.updateContainer(content, container) - }, - async 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 (options.initialContent !== undefined) { - instance.render(options.initialContent) - } - - return instance }