Convert tests to Vitest (#4)
This commit is contained in:
47
packages/website/app/routes/guides/buttons.md
Normal file
47
packages/website/app/routes/guides/buttons.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
order: 3
|
||||
meta:
|
||||
title: Buttons
|
||||
description: Using button components
|
||||
---
|
||||
|
||||
# Buttons
|
||||
|
||||
Use the `<Button />` component to create a message with a button, and use the `onClick` callback to respond to button clicks.
|
||||
|
||||
```jsx
|
||||
import { Button } from "reacord"
|
||||
|
||||
function Counter() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<>
|
||||
You've clicked the button {count} times.
|
||||
<Button label="+1" onClick={() => setCount(count + 1)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `onClick` callback receives an `event` object. It includes some information, such as the user who clicked the button, and functions for creating new replies in response. These functions return message instances.
|
||||
|
||||
```jsx
|
||||
import { Button } from "reacord"
|
||||
|
||||
function TheButton() {
|
||||
function handleClick(event) {
|
||||
const name = event.guild.member.displayName || event.user.username
|
||||
|
||||
const publicReply = event.reply(`${name} clicked the button. wow`)
|
||||
setTimeout(() => publicReply.destroy(), 3000)
|
||||
|
||||
const privateReply = event.ephemeralReply("good job, you clicked it")
|
||||
privateReply.deactivate() // we don't need to listen to updates on this
|
||||
}
|
||||
|
||||
return <Button label="click me i dare you" onClick={handleClick} />
|
||||
}
|
||||
```
|
||||
|
||||
See the [API reference](/api/index.html#ButtonProps) for more information.
|
||||
11
packages/website/app/routes/guides/custom-adapters.md
Normal file
11
packages/website/app/routes/guides/custom-adapters.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
meta:
|
||||
title: Using Reacord with other libraries
|
||||
description: Adapting Reacord to another Discord library
|
||||
---
|
||||
|
||||
# Using Reacord with other libraries
|
||||
|
||||
Reacord's core is built to be library agnostic, and can be adapted to libraries other than Discord.js. However, Discord.js is the only built-in adapter at the moment, and the adapter API is still a work in progress.
|
||||
|
||||
If you're interested in creating a custom adapter, [see the code for the Discord.js adapter as an example](https://github.com/itsMapleLeaf/reacord/blob/main/packages/reacord/library/core/reacord-discord-js.ts). Feel free to [create an issue on GitHub](https://github.com/itsMapleLeaf/reacord/issues/new) if you run into issues.
|
||||
64
packages/website/app/routes/guides/embeds.md
Normal file
64
packages/website/app/routes/guides/embeds.md
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
order: 2
|
||||
meta:
|
||||
title: Embeds
|
||||
description: Using embed components
|
||||
---
|
||||
|
||||
# Embeds
|
||||
|
||||
Reacord comes with an `<Embed />` component for sending rich embeds.
|
||||
|
||||
```jsx
|
||||
import { Embed } from "reacord"
|
||||
|
||||
function FancyMessage({ title, description }) {
|
||||
return (
|
||||
<Embed
|
||||
title={title}
|
||||
description={description}
|
||||
color={0x00ff00}
|
||||
timestamp={Date.now()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
reacord.send(channelId, <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
|
||||
import { Embed, EmbedTitle } from "reacord"
|
||||
|
||||
function FancyDetails({ title, description }) {
|
||||
return (
|
||||
<>
|
||||
<EmbedTitle>{title}</EmbedTitle>
|
||||
{/* embed descriptions are just text */}
|
||||
{description}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function FancyMessage({ children }) {
|
||||
return (
|
||||
<Embed color={0x00ff00} timestamp={Date.now()}>
|
||||
{children}
|
||||
</Embed>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
reacord.send(
|
||||
channelId,
|
||||
<FancyMessage>
|
||||
<FancyDetails title="Hello" description="World" />
|
||||
</FancyMessage>,
|
||||
)
|
||||
```
|
||||
|
||||
See the [API Reference](/api/index.html#EmbedAuthorProps) for the full list of embed components.
|
||||
44
packages/website/app/routes/guides/getting-started.md
Normal file
44
packages/website/app/routes/guides/getting-started.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
order: 0
|
||||
meta:
|
||||
title: Getting Started
|
||||
description: Learn how to get started with Reacord.
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
|
||||
This guide assumes some familiarity with 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.
|
||||
|
||||
**Note:** Ensure your project has support for running code with JSX. I recommend using [esno](https://npm.im/esno).
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install reacord react discord.js
|
||||
|
||||
# yarn
|
||||
yarn add reacord react discord.js
|
||||
|
||||
# pnpm
|
||||
pnpm add reacord react discord.js
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
Create a Discord.js client and a Reacord instance:
|
||||
|
||||
```js
|
||||
// main.js
|
||||
import { Client } from "discord.js"
|
||||
import { ReacordDiscordJs } from "reacord"
|
||||
|
||||
const client = new Client()
|
||||
const reacord = new ReacordDiscordJs(client)
|
||||
|
||||
client.on("ready", () => {
|
||||
console.log("Ready!")
|
||||
})
|
||||
|
||||
await client.login(process.env.BOT_TOKEN)
|
||||
```
|
||||
25
packages/website/app/routes/guides/links.md
Normal file
25
packages/website/app/routes/guides/links.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
order: 3
|
||||
meta:
|
||||
title: Links
|
||||
description: Using link components
|
||||
---
|
||||
|
||||
# Links
|
||||
|
||||
In Discord, links are a type of button, and they work similarly. Clicking on it leads you to the given URL. They only have one style, and can't be listened to for clicks.
|
||||
|
||||
```jsx
|
||||
import { Link } from "reacord"
|
||||
|
||||
function AwesomeLinks() {
|
||||
return (
|
||||
<>
|
||||
<Link label="look at this" url="https://google.com" />
|
||||
<Link label="wow" url="https://youtube.com/watch?v=dQw4w9WgXcQ" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
See the [API reference](/api/index.html#Link) for more information.
|
||||
71
packages/website/app/routes/guides/select-menu.md
Normal file
71
packages/website/app/routes/guides/select-menu.md
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
order: 4
|
||||
meta:
|
||||
title: Select Menus
|
||||
description: Using select menu components
|
||||
---
|
||||
|
||||
# Select Menus
|
||||
|
||||
To create a select menu, use the `Select` component, and pass a list of `Option` components as children. Use the `value` prop to set a currently selected value. You can respond to changes in the value via `onChangeValue`.
|
||||
|
||||
```jsx
|
||||
export function FruitSelect({ onConfirm }) {
|
||||
const [value, setValue] = useState()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Select
|
||||
placeholder="choose a fruit"
|
||||
value={value}
|
||||
onChangeValue={setValue}
|
||||
>
|
||||
<Option value="🍎" />
|
||||
<Option value="🍌" />
|
||||
<Option value="🍒" />
|
||||
</Select>
|
||||
<Button
|
||||
label="confirm"
|
||||
disabled={value == undefined}
|
||||
onClick={() => {
|
||||
if (value) onConfirm(value)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
const instance = reacord.send(
|
||||
channelId,
|
||||
<FruitSelect
|
||||
onConfirm={(value) => {
|
||||
instance.render(`you chose ${value}`)
|
||||
instance.deactivate()
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
```
|
||||
|
||||
For a multi-select, use the `multiple` prop, then you can use `values` and `onChangeMultiple` to handle multiple values.
|
||||
|
||||
```tsx
|
||||
export function FruitSelect({ onConfirm }) {
|
||||
const [values, setValues] = useState([])
|
||||
|
||||
return (
|
||||
<Select
|
||||
placeholder="choose a fruit"
|
||||
values={values}
|
||||
onChangeMultiple={setValues}
|
||||
>
|
||||
<Option value="🍎" />
|
||||
<Option value="🍌" />
|
||||
<Option value="🍒" />
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The Select component accepts a variety of props beyond what's shown here. See the [API reference](/api/index.html#SelectChangeEvent) for more information.
|
||||
167
packages/website/app/routes/guides/sending-messages.md
Normal file
167
packages/website/app/routes/guides/sending-messages.md
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
order: 1
|
||||
meta:
|
||||
title: Sending Messages
|
||||
description: Sending messages by creating Reacord instances
|
||||
---
|
||||
|
||||
# Sending Messages with Instances
|
||||
|
||||
You can send messages via Reacord to a channel like so.
|
||||
|
||||
```jsx
|
||||
const channelId = "abc123deadbeef"
|
||||
|
||||
client.on("ready", () => {
|
||||
reacord.send(channelId, "Hello, world!")
|
||||
})
|
||||
```
|
||||
|
||||
The `.send()` function creates a **Reacord instance**. You can pass strings, numbers, or anything that can be rendered by React, such as JSX!
|
||||
|
||||
Components rendered through this instance can include state and effects, and the message on Discord will update automatically.
|
||||
|
||||
```jsx
|
||||
function Uptime() {
|
||||
const [startTime] = useState(Date.now())
|
||||
const [currentTime, setCurrentTime] = useState(Date.now())
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
currentTime(Date.now())
|
||||
}, 3000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return <>this message has been shown for {currentTime - startTime}ms</>
|
||||
}
|
||||
|
||||
client.on("ready", () => {
|
||||
reacord.send(channelId, <Uptime />)
|
||||
})
|
||||
```
|
||||
|
||||
The instance can be rendered to multiple times, which will update the message each time.
|
||||
|
||||
```jsx
|
||||
const Hello = ({ subject }) => <>Hello, {subject}!</>
|
||||
|
||||
client.on("ready", () => {
|
||||
const instance = reacord.send(channel)
|
||||
instance.render(<Hello subject="World" />)
|
||||
instance.render(<Hello subject="Moon" />)
|
||||
})
|
||||
```
|
||||
|
||||
## Cleaning Up Instances
|
||||
|
||||
If you no longer want to use the instance, you can clean it up in a few ways:
|
||||
|
||||
- `instance.destroy()` - This will remove the message.
|
||||
- `instance.deactivate()` - This will keep the message, but it will disable the components on the message, and no longer listen to user interactions.
|
||||
|
||||
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
|
||||
const reacord = new ReacordDiscordJs(client, {
|
||||
// after sending four messages,
|
||||
// the first one will be deactivated
|
||||
maxInstances: 3,
|
||||
})
|
||||
```
|
||||
|
||||
## Discord Slash Commands
|
||||
|
||||
<aside>
|
||||
This section also applies to other kinds of application commands, such as context menu commands.
|
||||
</aside>
|
||||
|
||||
To reply to a command interaction, use the `.reply()` function. This function returns an instance that works the same way as the one from `.send()`. Here's an example:
|
||||
|
||||
```jsx
|
||||
import { Client } from "discord.js"
|
||||
import * as React from "react"
|
||||
import { Button, ReacordDiscordJs } from "reacord"
|
||||
|
||||
const client = new Client({ intents: [] })
|
||||
const reacord = new ReacordDiscordJs(client)
|
||||
|
||||
client.on("ready", () => {
|
||||
client.application?.commands.create({
|
||||
name: "ping",
|
||||
description: "pong!",
|
||||
})
|
||||
})
|
||||
|
||||
client.on("interactionCreate", (interaction) => {
|
||||
if (interaction.isCommand() && interaction.commandName === "ping") {
|
||||
// Use the reply() function instead of send
|
||||
reacord.reply(interaction, <>pong!</>)
|
||||
}
|
||||
})
|
||||
|
||||
client.login(process.env.DISCORD_TOKEN)
|
||||
```
|
||||
|
||||
<aside>
|
||||
This example uses <a href="https://discord.com/developers/docs/interactions/application-commands#registering-a-command">global commands</a>, so the command might take a while to show up 😅
|
||||
</aside>
|
||||
|
||||
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", () => {
|
||||
for (const { name, description } of commands) {
|
||||
client.application?.commands.create({ name, description })
|
||||
}
|
||||
})
|
||||
|
||||
client.on("interactionCreate", (interaction) => {
|
||||
if (interaction.isCommand()) {
|
||||
for (const command of commands) {
|
||||
if (interaction.commandName === command.name) {
|
||||
command.run(interaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
handleCommands(client, [
|
||||
{
|
||||
name: "ping",
|
||||
description: "pong!",
|
||||
run: (interaction) => {
|
||||
reacord.reply(interaction, <>pong!</>)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hi",
|
||||
description: "say hi",
|
||||
run: (interaction) => {
|
||||
reacord.reply(interaction, <>hi</>)
|
||||
},
|
||||
},
|
||||
])
|
||||
```
|
||||
|
||||
## Ephemeral Command Replies
|
||||
|
||||
Ephemeral replies are replies that only appear for one user. To create them, use the `.ephemeralReply()` function.
|
||||
|
||||
```tsx
|
||||
handleCommands(client, [
|
||||
{
|
||||
name: "pong",
|
||||
description: "pong, but in secret",
|
||||
run: (interaction) => {
|
||||
reacord.ephemeralReply(interaction, <>(pong)</>)
|
||||
},
|
||||
},
|
||||
])
|
||||
```
|
||||
|
||||
The `ephemeralReply` function also returns an instance, but ephemeral replies cannot be updated via `instance.render()`. You can `.deactivate()` them, but `.destroy()` will not delete the message; only the user can hide it from view.
|
||||
Reference in New Issue
Block a user