diff --git a/packages/reacord/helpers/prune-nullish-values.ts b/packages/reacord/helpers/prune-nullish-values.ts index 1bc28e2..2729990 100644 --- a/packages/reacord/helpers/prune-nullish-values.ts +++ b/packages/reacord/helpers/prune-nullish-values.ts @@ -12,7 +12,7 @@ export function pruneNullishValues(input: T): PruneNullishValues { const result: any = {} for (const [key, value] of Object.entries(input)) { if (value != undefined) { - result[key] = isObject(value) ? pruneNullishValues(value) : value + result[key] = pruneNullishValues(value) } } return result diff --git a/packages/reacord/library/core/instance-context.tsx b/packages/reacord/library/core/instance-context.tsx new file mode 100644 index 0000000..da1237f --- /dev/null +++ b/packages/reacord/library/core/instance-context.tsx @@ -0,0 +1,20 @@ +import * as React from "react" +import { raise } from "../../helpers/raise" +import type { ReacordInstance } from "./instance" + +const Context = React.createContext(undefined) + +export const InstanceProvider = Context.Provider + +/** + * Get the associated instance for the current component. + * + * @category Core + * @see https://reacord.fly.dev/guides/use-instance + */ +export function useInstance(): ReacordInstance { + return ( + React.useContext(Context) ?? + raise("Could not find instance, was this component rendered via Reacord?") + ) +} diff --git a/packages/reacord/library/core/reacord-discord-js.ts b/packages/reacord/library/core/reacord-discord-js.ts index 466689e..d9d1955 100644 --- a/packages/reacord/library/core/reacord-discord-js.ts +++ b/packages/reacord/library/core/reacord-discord-js.ts @@ -154,14 +154,16 @@ export class ReacordDiscordJs extends Reacord { // todo please dear god clean this up const channel: ChannelInfo = interaction.channel ? { - ...pick(pruneNullishValues(interaction.channel), [ - "topic", - "nsfw", - "lastMessageId", - "ownerId", - "parentId", - "rateLimitPerUser", - ]), + ...pruneNullishValues( + pick(interaction.channel, [ + "topic", + "nsfw", + "lastMessageId", + "ownerId", + "parentId", + "rateLimitPerUser", + ]), + ), id: interaction.channelId, } : raise("Non-channel interactions are not supported") @@ -190,15 +192,17 @@ export class ReacordDiscordJs extends Reacord { const member: GuildMemberInfo | undefined = interaction.member instanceof Discord.GuildMember ? { - ...pick(pruneNullishValues(interaction.member), [ - "id", - "nick", - "displayName", - "avatarUrl", - "displayAvatarUrl", - "color", - "pending", - ]), + ...pruneNullishValues( + pick(interaction.member, [ + "id", + "nick", + "displayName", + "avatarUrl", + "displayAvatarUrl", + "color", + "pending", + ]), + ), displayName: interaction.member.displayName, roles: [...interaction.member.roles.cache.map((role) => role.id)], joinedAt: interaction.member.joinedAt?.toISOString(), @@ -210,18 +214,15 @@ export class ReacordDiscordJs extends Reacord { const guild: GuildInfo | undefined = interaction.guild ? { - ...pick(pruneNullishValues(interaction.guild), ["id", "name"]), + ...pruneNullishValues(pick(interaction.guild, ["id", "name"])), member: member ?? raise("unexpected: member is undefined"), } : undefined const user: UserInfo = { - ...pick(pruneNullishValues(interaction.user), [ - "id", - "username", - "discriminator", - "tag", - ]), + ...pruneNullishValues( + pick(interaction.user, ["id", "username", "discriminator", "tag"]), + ), avatarUrl: interaction.user.avatarURL()!, accentColor: interaction.user.accentColor ?? undefined, } diff --git a/packages/reacord/library/core/reacord.ts b/packages/reacord/library/core/reacord.tsx similarity index 85% rename from packages/reacord/library/core/reacord.ts rename to packages/reacord/library/core/reacord.tsx index b1de60c..d402ffe 100644 --- a/packages/reacord/library/core/reacord.ts +++ b/packages/reacord/library/core/reacord.tsx @@ -1,8 +1,10 @@ import type { ReactNode } from "react" +import React from "react" import type { ComponentInteraction } from "../internal/interaction" import { reconciler } from "../internal/reconciler.js" import type { Renderer } from "../internal/renderers/renderer" import type { ReacordInstance } from "./instance" +import { InstanceProvider } from "./instance-context" /** * @category Core @@ -47,13 +49,12 @@ export abstract class Reacord { const container = reconciler.createContainer(renderer, 0, false, {}) - if (initialContent !== undefined) { - reconciler.updateContainer(initialContent, container) - } - - return { + const instance: ReacordInstance = { render: (content: ReactNode) => { - reconciler.updateContainer(content, container) + reconciler.updateContainer( + {content}, + container, + ) }, deactivate: () => { this.deactivate(renderer) @@ -63,6 +64,12 @@ export abstract class Reacord { renderer.destroy() }, } + + if (initialContent !== undefined) { + instance.render(initialContent) + } + + return instance } private deactivate(renderer: Renderer) { diff --git a/packages/reacord/library/main.ts b/packages/reacord/library/main.ts index 536d7c6..8ae77f1 100644 --- a/packages/reacord/library/main.ts +++ b/packages/reacord/library/main.ts @@ -13,5 +13,6 @@ export * from "./core/components/link" export * from "./core/components/option" export * from "./core/components/select" export * from "./core/instance" +export { useInstance } from "./core/instance-context" export * from "./core/reacord" export * from "./core/reacord-discord-js" diff --git a/packages/reacord/package.json b/packages/reacord/package.json index 1ec2a59..9ad570e 100644 --- a/packages/reacord/package.json +++ b/packages/reacord/package.json @@ -23,14 +23,14 @@ "test": "vitest --coverage --no-watch", "test-dev": "vitest", "typecheck": "tsc --noEmit", - "playground": "nodemon --exec esmo --ext ts,tsx ./playground/main.tsx", + "playground": "nodemon --exec esmo --ext ts,tsx --inspect=5858 --enable-source-maps ./playground/main.tsx", "release": "release-it" }, "dependencies": { "@types/node": "*", "@types/react": "*", "@types/react-reconciler": "^0.26.4", - "nanoid": "^3.1.30", + "nanoid": "^3.1.31", "react-reconciler": "^0.26.2", "rxjs": "^7.5.2" }, @@ -47,7 +47,7 @@ "@types/lodash-es": "^4.17.5", "c8": "^7.11.0", "discord.js": "^13.5.1", - "dotenv": "^10.0.0", + "dotenv": "^11.0.0", "esbuild": "latest", "esbuild-jest": "^0.5.0", "esmo": "^0.13.0", @@ -61,7 +61,7 @@ "type-fest": "^2.9.0", "typescript": "^4.5.4", "vite": "^2.7.10", - "vitest": "^0.0.140" + "vitest": "^0.0.141" }, "resolutions": { "esbuild": "latest" diff --git a/packages/reacord/playground/main.tsx b/packages/reacord/playground/main.tsx index b16b72f..2e3492d 100644 --- a/packages/reacord/playground/main.tsx +++ b/packages/reacord/playground/main.tsx @@ -1,7 +1,7 @@ import { Client } from "discord.js" import "dotenv/config" import React from "react" -import { Button, ReacordDiscordJs } from "../library/main" +import { Button, ReacordDiscordJs, useInstance } from "../library/main" import { createCommandHandler } from "./command-handler" import { Counter } from "./counter" import { FruitSelect } from "./fruit-select" @@ -93,6 +93,17 @@ createCommandHandler(client, [ ) }, }, + { + name: "delete-this", + description: "delete this", + run: (interaction) => { + function DeleteThis() { + const instance = useInstance() + return