Skip to content

Commit

Permalink
Merge pull request #43 from domin-mnd/main
Browse files Browse the repository at this point in the history
  • Loading branch information
itsMapleLeaf authored Nov 1, 2023
2 parents a00fbc0 + 2a8ee78 commit b641885
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 73 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,21 @@ pnpm add reacord react discord.js

<!-- prettier-ignore -->
```tsx
import * as React from "react"
import { useState } from "react"
import { Embed, Button } from "reacord"

function Counter() {
const [count, setCount] = React.useState(0)
const [count, setCount] = useState(0)

return (
<>
<Embed title="Counter">
This button has been clicked {count} times.
</Embed>
<Button onClick={() => setCount(count + 1)}>
+1
</Button>
<Button
label="+1"
onClick={() => setCount(count + 1)}
/>
</>
)
}
Expand Down
55 changes: 52 additions & 3 deletions packages/reacord/scripts/discordjs-manual-test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { raise } from "@reacord/helpers/raise.js"
import {
Button,
Embed,
EmbedField,
Link,
Option,
ReacordDiscordJs,
Expand All @@ -11,7 +13,6 @@ import type { TextChannel } from "discord.js"
import { ChannelType, Client, IntentsBitField } from "discord.js"
import "dotenv/config"
import { kebabCase } from "lodash-es"
import * as React from "react"
import { useState } from "react"

const client = new Client({ intents: IntentsBitField.Flags.Guilds })
Expand Down Expand Up @@ -53,9 +54,57 @@ await createTest("basic", (channel) => {
reacord.createChannelMessage(channel).render("Hello, world!")
})

await createTest("readme counter", (channel) => {
interface EmbedCounterProps {
count: number
visible: boolean
}

function EmbedCounter({ count, visible }: EmbedCounterProps) {
if (!visible) return <></>

return (
<Embed title="the counter">
<EmbedField name="is it even?">{count % 2 ? "no" : "yes"}</EmbedField>
</Embed>
)
}

function Counter() {
const [showEmbed, setShowEmbed] = useState(false)
const [count, setCount] = useState(0)
const instance = useInstance()

return (
<>
this button was clicked {count} times
<EmbedCounter count={count} visible={showEmbed} />
<Button
style="primary"
label="clicc"
onClick={() => setCount(count + 1)}
/>
<Button
style="secondary"
label={showEmbed ? "hide embed" : "show embed"}
onClick={() => setShowEmbed(!showEmbed)}
/>
<Button
style="danger"
label="deactivate"
onClick={() => instance.destroy()}
/>
</>
)
}

reacord.createChannelMessage(channel).render(<Counter />)
})

await createTest("counter", (channel) => {
const Counter = () => {
const [count, setCount] = React.useState(0)
function Counter() {
const [count, setCount] = useState(0)

return (
<>
count: {count}
Expand Down
23 changes: 4 additions & 19 deletions packages/website/src/content/guides/0-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ slug: getting-started

# Getting Started

These guides assume some familiarity with [JavaScript](https://developer.mozilla.org/en-US/docs/Web/javascript), [React](https://reactjs.org), [Discord.js](https://discord.js.org) and the [Discord API](https://discord.dev). Keep these pages as reference if you need it.
These guides assume some familiarity with [JavaScript](https://developer.mozilla.org/en-US/docs/Web/javascript), [TypeScript](https://www.typescriptlang.org/), [React](https://reactjs.org), [Discord.js](https://discord.js.org) and the [Discord API](https://discord.dev). Keep these pages as reference if you need it.

## Setup from template

Expand All @@ -29,31 +29,16 @@ pnpm add reacord react discord.js

Create a Discord.js client and a Reacord instance:

```js
// main.jsx
import { Client } from "discord.js"
```ts
import { Client, Events } from "discord.js"
import { ReacordDiscordJs } from "reacord"

const client = new Client()
const reacord = new ReacordDiscordJs(client)

client.on("ready", () => {
client.once(Events.ClientReady, () => {
console.log("Ready!")
})

await client.login(process.env.BOT_TOKEN)
```

To use JSX in your code, run it with [tsx](https://npm.im/tsx):

```bash
npm install -D tsx
npx tsx main.tsx
```

For production, I recommend compiling it with [tsup](https://npm.im/tsup):

```bash
npm install -D tsup
npx tsup src/main.tsx --target node20
```
59 changes: 38 additions & 21 deletions packages/website/src/content/guides/1-sending-messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ slug: sending-messages

You can send messages via Reacord to a channel like so.

```jsx
client.on("ready", () => {
```tsx
client.once(Events.ClientReady, () => {
const channel = await client.channels.fetch("abc123deadbeef")
reacord.createChannelMessage(channel).render("Hello, world!")
})
Expand All @@ -19,7 +19,9 @@ The `.createChannelMessage()` function creates a **Reacord instance**. You can p

Components rendered through this instance can include state and effects, and the message on Discord will update automatically.

```jsx
```tsx
import { useEffect, useState } from "react"

function Uptime() {
const [startTime] = useState(Date.now())
const [currentTime, setCurrentTime] = useState(Date.now())
Expand All @@ -34,18 +36,22 @@ function Uptime() {
return <>this message has been shown for {currentTime - startTime}ms</>
}

client.on("ready", () => {
client.once(Events.ClientReady, () => {
const instance = reacord.createChannelMessage(channel)
instance.render(<Uptime />)
})
```

The instance can be rendered to multiple times, which will update the message each time.

```jsx
const Hello = ({ subject }) => <>Hello, {subject}!</>
```tsx
interface HelloProps {
subject: string
}

const Hello = ({ subject }: HelloProps) => <>Hello, {subject}!</>

client.on("ready", () => {
client.once(Events.ClientReady, () => {
const instance = reacord.createChannelMessage(channel)
instance.render(<Hello subject="World" />)
instance.render(<Hello subject="Moon" />)
Expand All @@ -54,7 +60,7 @@ client.on("ready", () => {

You can specify various options for the message:

```jsx
```tsx
const instance = reacord.createChannelMessage(channel, {
tts: true,
reply: {
Expand All @@ -75,7 +81,7 @@ If you no longer want to use the instance, you can clean it up in a few ways:

By default, Reacord has a max limit on the number of active instances, and deactivates older instances to conserve memory. This can be configured through the Reacord options:

```js
```ts
const reacord = new ReacordDiscordJs(client, {
// after sending four messages,
// the first one will be deactivated
Expand All @@ -91,29 +97,29 @@ This section also applies to other kinds of application commands, such as contex

To reply to a command interaction, use the `.createInteractionReply()` function. This function returns an instance that works the same way as the one from `.createChannelMessage()`. Here's an example:

```jsx
import { Client } from "discord.js"
```tsx
import { Client, Events } from "discord.js"
import { Button, ReacordDiscordJs } from "reacord"
import * as React from "react"

const client = new Client({ intents: [] })
const reacord = new ReacordDiscordJs(client)

client.on("ready", () => {
client.once(Events.ClientReady, () => {
client.application?.commands.create({
name: "ping",
description: "pong!",
})
})

client.on("interactionCreate", (interaction) => {
client.on(Events.InteractionCreate, (interaction) => {
if (interaction.isCommand() && interaction.commandName === "ping") {
// Use the createInteractionReply() function instead of createChannelMessage
reacord.createInteractionReply(interaction).render(<>pong!</>)
}
})

client.login(process.env.DISCORD_TOKEN)
await client.login(process.env.DISCORD_TOKEN)
```

<aside>
Expand All @@ -122,15 +128,26 @@ This example uses <a href="https://discord.com/developers/docs/interactions/appl

However, the process of creating commands can get really repetitive and error-prone. A command framework could help with this, or you could make a small helper:

```jsx
function handleCommands(client, commands) {
client.on("ready", () => {
```tsx
import type { Client, CommandInteraction } from "discord.js"

interface Command {
// Command name
name: string
// A mandatory description for the command
description: string
// Specific handler for the command
run: (interaction: CommandInteraction) => Promise<void> | void
}

function handleCommands(client: Client, commands: Command[]) {
client.once(Events.ClientReady, () => {
for (const { name, description } of commands) {
client.application?.commands.create({ name, description })
}
})

client.on("interactionCreate", (interaction) => {
client.on(Events.InteractionCreate, (interaction) => {
if (interaction.isCommand()) {
for (const command of commands) {
if (interaction.commandName === command.name) {
Expand All @@ -142,7 +159,7 @@ function handleCommands(client, commands) {
}
```

```jsx
```tsx
handleCommands(client, [
{
name: "ping",
Expand All @@ -165,7 +182,7 @@ handleCommands(client, [

Ephemeral replies are replies that only appear for one user. To create them, use the `.createInteractionReply()` function and provide `ephemeral` option.

```jsx
```tsx
handleCommands(client, [
{
name: "pong",
Expand All @@ -183,7 +200,7 @@ handleCommands(client, [

Additionally interaction replies may have `tts` option to turn on text-to-speech ability for the reply. To create such reply, use `.createInteractionReply()` function and provide `tts` option.

```jsx
```tsx
handleCommands(client, [
{
name: "pong",
Expand Down
28 changes: 21 additions & 7 deletions packages/website/src/content/guides/2-embeds.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ slug: embeds

Reacord comes with an `<Embed />` component for sending rich embeds.

```jsx
```tsx
import { Embed } from "reacord"

function FancyMessage({ title, description }) {
interface FancyMessageProps {
title: string
description: string
}

function FancyMessage({ title, description }: FancyMessageProps) {
return (
<Embed
title={title}
Expand All @@ -23,18 +28,23 @@ function FancyMessage({ title, description }) {
}
```

```jsx
```tsx
reacord
.createChannelMessage(channel)
.render(<FancyMessage title="Hello" description="World" />)
```

Reacord also comes with multiple embed components, for defining embeds on a piece-by-piece basis. This enables composition:

```jsx
```tsx
import { Embed, EmbedTitle } from "reacord"

function FancyDetails({ title, description }) {
interface FancyDetailsProps {
title: string
description: string
}

function FancyDetails({ title, description }: FancyDetailsProps) {
return (
<>
<EmbedTitle>{title}</EmbedTitle>
Expand All @@ -44,7 +54,11 @@ function FancyDetails({ title, description }) {
)
}

function FancyMessage({ children }) {
interface FancyMessageProps {
children: React.ReactNode
}

function FancyMessage({ children }: FancyMessageProps) {
return (
<Embed color={0x00ff00} timestamp={Date.now()}>
{children}
Expand All @@ -53,7 +67,7 @@ function FancyMessage({ children }) {
}
```

```jsx
```tsx
reacord.createChannelMessage(channel).render(
<FancyMessage>
<FancyDetails title="Hello" description="World" />
Expand Down
Loading

0 comments on commit b641885

Please sign in to comment.