Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use TS for documentation guides #43

Merged
merged 4 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading