Docs/components v2 :wires: (#3162)
* new pages :3 * fimished intro page * fimished interaction page * remove unused shit * I think we are done lmao * I lied, fixed some small mistakes * Update docs/guides/components_v2/interaction.md Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> * misha quality assurance :3 + breakings pages * Apply suggestions from code review Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> * component types guide expanded * :3 * Apply suggestions from code review Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> --------- Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com>
This commit is contained in:
2
.github/ISSUE_TEMPLATE/bugreport.yml
vendored
2
.github/ISSUE_TEMPLATE/bugreport.yml
vendored
@@ -10,7 +10,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Check The Docs
|
label: Check The Docs
|
||||||
description: Please refer to our [FAQs](https://discordnet.dev/faq/basics/getting-started.html), [Documentation](https://discordnet.dev/api/index.html),
|
description: Please refer to our [FAQs](https://discordnet.dev/faq/basics/getting-started.html), [Documentation](https://discordnet.dev/api/index.html),
|
||||||
and [Migration Guide](https://discordnet.dev/guides/v2_v3_guide/v2_to_v3_guide.html) before reporting issues.
|
and [Migration Guide](https://discordnet.dev/guides/breakings/v2_to_v3_guide.html) before reporting issues.
|
||||||
options:
|
options:
|
||||||
- label: "I double checked the docs and couldn't find any useful information."
|
- label: "I double checked the docs and couldn't find any useful information."
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
uid: Guides.V2V3Guide
|
uid: Guides.Breakings.V2V3Guide
|
||||||
title: V2 -> V3 Guide
|
title: V2 -> V3 Guide
|
||||||
---
|
---
|
||||||
|
|
||||||
40
docs/guides/breakings/v3.18.md
Normal file
40
docs/guides/breakings/v3.18.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
uid: Guides.Breakings.V3_18
|
||||||
|
title: V3.18
|
||||||
|
---
|
||||||
|
|
||||||
|
# V3.18
|
||||||
|
|
||||||
|
Among the changes in V3.18 was components V2, while splendid, it did bring with it a few changes you should be aware of.
|
||||||
|
|
||||||
|
- IMessage
|
||||||
|
|
||||||
|
Messages components type has changed. As a patch you can convert it to an `IEnumerable<ActionRowComponent>` again like so:
|
||||||
|
```cs
|
||||||
|
IMessage message = component.Message;
|
||||||
|
IReadOnlyCollection<IMessageComponent>? components = message.Components;
|
||||||
|
IEnumerable<ActionRowComponent>? componentsLegacy = components.OfType<ActionRowComponent>();
|
||||||
|
```
|
||||||
|
|
||||||
|
- Nested components
|
||||||
|
|
||||||
|
The usage of components with `ActionRowBuilder` and `ComponentBuilder` has a type change as well. You can find examples in [Components_V2_Advanced].
|
||||||
|
|
||||||
|
- Components V2 Flag
|
||||||
|
|
||||||
|
To use Components V2 in a message (through `ModifyAsync`/`UpdateAsync`/...), a specific flag has to be set. This cannot be undone though :^). You only have to set it if you modify a message that didn't have components v2 in them to begin with (or if you already set the flag manually - obviously). This means that if you created a message with your bot that has components in it, the flag will automatically be set.
|
||||||
|
|
||||||
|
Otherwise, you can manually set it like so:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
MessageFlags? flags = component.Message.Flags ?? MessageFlags.None;
|
||||||
|
flags = flags | MessageFlags.ComponentsV2;
|
||||||
|
|
||||||
|
await component.UpdateAsync(m =>
|
||||||
|
{
|
||||||
|
m.Flags = flags;
|
||||||
|
m.Components = cv2builder.Build();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Components_V2_Advanced]: xref:Guides.ComponentsV2.Advanced
|
||||||
136
docs/guides/components_v2/advanced.md
Normal file
136
docs/guides/components_v2/advanced.md
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
---
|
||||||
|
uid: Guides.ComponentsV2.Advanced
|
||||||
|
title: Create Components V2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Component types
|
||||||
|
|
||||||
|
As denoted in [Intro], This framework supports a lot of component types.
|
||||||
|
|
||||||
|
A full list of components available:
|
||||||
|
|
||||||
|
| Type | Name | Description | Components V2 only (flag)
|
||||||
|
|------|---------------------|-------------------------------------------------------------------|---------------------------|
|
||||||
|
| 1 | Action Row | Container to display a row of interactive components | No |
|
||||||
|
| 2 | Button | Button object | No |
|
||||||
|
| 3 | String Select | Select menu for picking from defined text options | No |
|
||||||
|
| 4 | Text Input | Text input object | No |
|
||||||
|
| 5 | User Select | Select menu for users | No |
|
||||||
|
| 6 | Role Select | Select menu for roles | No |
|
||||||
|
| 7 | Mentionable Select | Select menu for mentionables (users and roles) | No |
|
||||||
|
| 8 | Channel Select | Select menu for channels | No |
|
||||||
|
| 9 | Section | Container to display text alongside an accessory component | Yes |
|
||||||
|
| 10 | Text Display | Markdown text | Yes |
|
||||||
|
| 11 | Thumbnail | Small image that can be used as an accessory | Yes |
|
||||||
|
| 12 | Media Gallery | Display images and other media | Yes |
|
||||||
|
| 13 | File | Displays an attached file | Yes |
|
||||||
|
| 14 | Separator | Component to add vertical padding between other components | Yes |
|
||||||
|
| 17 | Container | Container that visually groups a set of components | Yes
|
||||||
|
|
||||||
|
All components have an id field (generated by default) and must be unique within the message. Generation of ids won't use another id that exists in the message if you have one defined for another component. Sending components with an id of `0` is allowed but will be treated as empty and replaced by the API. You may want to manually assign one if you want to later identify a component this way. Some (interactive) components can be identified using a `customId` instead, which is a unique string (max 100 chars).
|
||||||
|
|
||||||
|
**[Action Row](https://discord.com/developers/docs/components/reference#action-row)**
|
||||||
|
|
||||||
|
The `ActionRow` component is a parent that contains either
|
||||||
|
- 1 to 5 `Buttons`
|
||||||
|
- 1 `TextInput`
|
||||||
|
- 1 of (`StringSelect`, `UserSelect`, `RoleSelect`, `MentionableSelect`, `ChannelSelect`)
|
||||||
|
|
||||||
|
**[Button](https://discord.com/developers/docs/components/reference#button)**
|
||||||
|
|
||||||
|
- Must be inside an `ActionRow` or in the accessory field of a `Section`
|
||||||
|
- Non-link and non-premium buttons must have a custom_id, and cannot have a url or a sku_id.
|
||||||
|
- Link buttons must have a url, and cannot have a custom_id
|
||||||
|
- Link buttons do not send an interaction to your app when clicked
|
||||||
|
- Premium buttons must contain a sku_id, and cannot have a custom_id, label, url, or emoji.
|
||||||
|
- Premium buttons do not send an interaction to your app when clicked
|
||||||
|
|
||||||
|
**[String Select](https://discord.com/developers/docs/components/reference#string-select)** (also other Select variants)
|
||||||
|
- Must be inside an `ActionRow`
|
||||||
|
- 1 to 25 `options`
|
||||||
|
- Placeholder (if specified, up to 150 chars)
|
||||||
|
|
||||||
|
It supports multi-select. If you want to use this functionality, you must set `min_values` (0 - 25) and `max_values` (1 - 25).
|
||||||
|
|
||||||
|
For all select options:
|
||||||
|
- Label, value and optionally the description may not exceed `100` chars
|
||||||
|
|
||||||
|
**[Text Input](https://discord.com/developers/docs/components/reference#text-input)**
|
||||||
|
- Must be inside an `ActionRow` in a `Modal`
|
||||||
|
- Label has a max length of `45`
|
||||||
|
|
||||||
|
By default it will accept up to `4000` characters (this is also the maximum). You can change this using the `min_values` and `max_values`
|
||||||
|
|
||||||
|
**[Section](https://discord.com/developers/docs/components/reference#section)**
|
||||||
|
|
||||||
|
A parent component, this allows you to put `TextDisplays` (1 - 3) next to eachother. It includes an accessory field, which can be used to display a `Thumbnail` or `Button`.
|
||||||
|
|
||||||
|
**[Text Display](https://discord.com/developers/docs/components/reference#text-display)**
|
||||||
|
|
||||||
|
Just text lol. (With markdown support, just like a regular user; up to 4000 characters)
|
||||||
|
|
||||||
|
**[Thumbnail](https://discord.com/developers/docs/components/reference#thumbnail)**
|
||||||
|
|
||||||
|
A small image (to use in a `Section`).
|
||||||
|
- Optionally has a description of max `1024` chars
|
||||||
|
|
||||||
|
**[Media Gallery](https://discord.com/developers/docs/components/reference#media-gallery)**
|
||||||
|
|
||||||
|
A component used to display up to `10` images.
|
||||||
|
- Each image can have a description of max `1024` chars
|
||||||
|
|
||||||
|
**[File](https://discord.com/developers/docs/components/reference#file)**
|
||||||
|
|
||||||
|
Used to send a single file. Only supports the `attachment://` protocol.
|
||||||
|
|
||||||
|
**[Separator](https://discord.com/developers/docs/components/reference#separator)**
|
||||||
|
|
||||||
|
Just something to put space between components (Y axis).
|
||||||
|
- Spacing can be set to either `1` (small space) or `2` (large space)
|
||||||
|
- Visibility can be toggled too. (`IsDivider`)
|
||||||
|
|
||||||
|
**[Container](https://discord.com/developers/docs/components/reference#container)**
|
||||||
|
|
||||||
|
A parent component with a side bar of customisable colour (like embeds).
|
||||||
|
|
||||||
|
In Discord.NET, you typically use these components in conjunction with a `ComponentBuilderV2`. The V2 specific components can be added to the builder using the `WithX` fluent/chain methods whereas the other supported components are mostly children of `ActionRows` and can be added as a component array like used below. You need to know what components can be added to which component though to prevent errors (this is why the above sections exist). If your component structure is wrong, Discord.NET will throw an exception.
|
||||||
|
|
||||||
|
This example offers some more insight on how to use them. Below is a component with `TextDisplay`, `MediaGallery` and `ActionRow` (with `Buttons` or `SelectMenu`).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
Some code will not be included here as it is not relevant to this framework. If you want to see the full code, it is [here](https://github.com/Adrigorithm/Adribot/).
|
||||||
|
|
||||||
|
The main component container generation method:
|
||||||
|
[!code-csharp[ComponentBuilderV2 Sample](samples/component.cs)]
|
||||||
|
|
||||||
|
## Interactions
|
||||||
|
|
||||||
|
The button triggers the following modal
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[!code-csharp[Modal Sample](samples/recipe-servings-modal.cs)]
|
||||||
|
|
||||||
|
Interactions used by this message:
|
||||||
|
|
||||||
|
[!code-csharp[Interaction Sample](samples/recipe-interactions.cs)]
|
||||||
|
|
||||||
|
After the **SET SERVINGS** modal is submitted (or the COMBOXBOX is changed) the UI is updated:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common issues
|
||||||
|
- Your interaction may not work if you use components v2 using `ModifyAsync`/`UpdateAsync`/..., if that is the case you must set the MessageFlags.ComponentsV2 flag on the message as mentioned in [CV2_Flag].
|
||||||
|
- Currently there may be some rare instances where the flag may not be set even when it should, as per [CV2_Flag]. An example is when more than 5 action rows are used. This might get fixed later though. You should set it manually in this case (You can identify this using method described below).
|
||||||
|
|
||||||
|
If you run into any trouble (appliction not responding when sending components), a debugger is (like usually) a useful tool to have at your disposal. More specifically within this context: dnet will do some checks before sending the component configuration to discord, so on building the component array, you can check for errors thrown. If this does not help, setting a breakpoint on the line that sends your component to discord (ModifyAsync/RespondAsync/...) and stepping to the next line (within 3 seconds of triggering it) may yield a more specific error returned by Discord itself.
|
||||||
|
|
||||||
|
[Intro]: xref:Guides.ComponentsV2.Intro
|
||||||
|
[CV2_Flag]: xref:Guides.Breakings.V3_18
|
||||||
BIN
docs/guides/components_v2/images/basic-component-ui.png
Normal file
BIN
docs/guides/components_v2/images/basic-component-ui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 362 KiB |
BIN
docs/guides/components_v2/images/interaction-response.png
Normal file
BIN
docs/guides/components_v2/images/interaction-response.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 371 KiB |
BIN
docs/guides/components_v2/images/interaction.png
Normal file
BIN
docs/guides/components_v2/images/interaction.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 367 KiB |
BIN
docs/guides/components_v2/images/modal.png
Normal file
BIN
docs/guides/components_v2/images/modal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
BIN
docs/guides/components_v2/images/updated-ingredients.png
Normal file
BIN
docs/guides/components_v2/images/updated-ingredients.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
BIN
docs/guides/components_v2/images/updated-oven.png
Normal file
BIN
docs/guides/components_v2/images/updated-oven.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
28
docs/guides/components_v2/interaction.md
Normal file
28
docs/guides/components_v2/interaction.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
uid: Guides.ComponentsV2.Interaction
|
||||||
|
title: Interact with Components V2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Interaction with components
|
||||||
|
|
||||||
|
## Lifecycle
|
||||||
|
A component should receive an initial response within a 3 second timeframe. After this it can continue receiving responses for up to 15 minutes, this is useful when a component needs to be rebuilt periodically (buttons etc), which is also what we will be leveraging here.
|
||||||
|
|
||||||
|
## Catching of and responding to a user interaction
|
||||||
|
|
||||||
|
Before we respond to an interaction triggered by the user we must first "catch" it. You can do this by hooking into the `DiscordSocketClient#InteractionCreated` event. Before proceeding we should make sure that the event was triggered by our component.
|
||||||
|
|
||||||
|
The way you get you get/retrieve a specific component from a message is by either their `customId` (this is what is used here) or by using `IEnumerable<IMessageComponent>#FindComponentById`. The latter finds a component by the integer id, and optionally by the type provided as the generic type parameter. Each component is automatically assigned with an incremental id (unique within a given message), unless overriden by the developer. You can see how that works in [Advanced].
|
||||||
|
|
||||||
|
Consider this component (the same as used in [Intro]). The buttons have a customId of "recipes-show-me-button-{recipe.RecipeId}", where the last part is an unique identifier.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[!code-csharp[Interactions Sample](samples/interactions.cs)]
|
||||||
|
|
||||||
|
UpdateAsync replaces our component array with a new one built based on the button clicked (recipe with the specified ID). More on this more advanced component v2 in [Advanced].
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[Intro]: xref:Guides.ComponentsV2.Intro
|
||||||
|
[Advanced]: xref:Guides.ComponentsV2.Advanced
|
||||||
26
docs/guides/components_v2/intro.md
Normal file
26
docs/guides/components_v2/intro.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
uid: Guides.ComponentsV2.Intro
|
||||||
|
title: Start using Components V2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Message Components V2
|
||||||
|
|
||||||
|
Message components V2 is, not unlike Message components (V1) a framework for adding interactive elements to a message your app or bot sends. They're accessible, customizable, and easy to use. Unlike its predecessor, V2 allows for much more control on how and where to display images, ~~buttons~~ bottoms, comboxes and more within a message :3.
|
||||||
|
|
||||||
|
## What is a Component
|
||||||
|
|
||||||
|
Components are a parameter you can use when sending messages with your bot (just like normal components). With V2 you have a lot more components to choose from though :3. A full list of compatible components can be found [here](https://discord.com/developers/docs/components/reference#component-object-component-types).
|
||||||
|
|
||||||
|
## Creating components
|
||||||
|
|
||||||
|
Let's create a simple component (array) to start with. First thing we need is a way to trigger the message, this can be done via commands or simply a ready event. Lets make a command that triggers our message.
|
||||||
|
|
||||||
|
[!code-csharp[Command Sample](samples/recipes-command.cs)]
|
||||||
|
|
||||||
|
The code below returns the value for `recipeService.GetRecipesComponentAsync()`.
|
||||||
|
|
||||||
|
[!code-csharp[ComponentBuilderV2 Sample](samples/recipes-component.cs)]
|
||||||
|
|
||||||
|
This ComponentBuilderV2 is used to create a message with a text field, an image and a button. Take note on the `customId` property of the `Button`: `$"{RecipesLookInsideButton}-{recipe.RecipeId}"`, it is always important to ensure it is unique within a message, so we can respond to the user interacting with it. More on that later.
|
||||||
|
|
||||||
|

|
||||||
57
docs/guides/components_v2/samples/component.cs
Normal file
57
docs/guides/components_v2/samples/component.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
private static ComponentBuilderV2 BuildComponentUnsafe(Recipe recipe, Units units = Units.Si)
|
||||||
|
{
|
||||||
|
StringBuilder ingredients = new($"## Ingredients{Environment.NewLine}");
|
||||||
|
StringBuilder instructions = new($"## Instructions{Environment.NewLine}");
|
||||||
|
ButtonBuilder servingsModalButton = new ButtonBuilder()
|
||||||
|
.WithCustomId(RecipeServingsModal)
|
||||||
|
.WithLabel("Set servings")
|
||||||
|
.WithStyle(ButtonStyle.Primary);
|
||||||
|
|
||||||
|
foreach (RecipeIngredient recipeIngredient in recipe.RecipeIngredients)
|
||||||
|
{
|
||||||
|
ingredients.Append($"`{recipeIngredient.Quantity} {recipeIngredient.Unit.ToSymbol()}` {recipeIngredient.Ingredient.Name} ");
|
||||||
|
|
||||||
|
if (recipeIngredient.Optional)
|
||||||
|
ingredients.AppendLine("[Optional]");
|
||||||
|
else
|
||||||
|
ingredients.Append(Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < recipe.Instruction.Length; i++)
|
||||||
|
instructions.AppendLine($"`{i + 1}.` {recipe.Instruction[i]}{Environment.NewLine}");
|
||||||
|
|
||||||
|
return new ComponentBuilderV2()
|
||||||
|
.WithTextDisplay($"# {recipe.Name}", RecipeNameDisplay)
|
||||||
|
.WithTextDisplay($"-# {recipe.Servings} servings", RecipeServingsDisplay)
|
||||||
|
.WithMediaGallery([
|
||||||
|
"https://cdn.discordapp.com/attachments/964253122547552349/1336440069892083712/7Q3S.gif?ex=67a3d04e&is=67a27ece&hm=059c9d28466f43a50c4b450ca26fc01298a2080356421d8524384bf67ea8f3ab&"
|
||||||
|
])
|
||||||
|
.WithActionRow([servingsModalButton])
|
||||||
|
.WithTextDisplay(ingredients.ToString())
|
||||||
|
.WithTextDisplay($"""
|
||||||
|
## Oven Settings
|
||||||
|
Mode: `{recipe.OvenMode.ToHumanReadable()}`
|
||||||
|
Temperature: `{recipe.Temperature.Convert(Unit.Temperature, Units.Si, units)} {units.ToSymbol()}`
|
||||||
|
""")
|
||||||
|
.WithActionRow([
|
||||||
|
new SelectMenuBuilder(
|
||||||
|
RecipeUnitInput,
|
||||||
|
options:[
|
||||||
|
new SelectMenuOptionBuilder(
|
||||||
|
"Metric",
|
||||||
|
"1",
|
||||||
|
isDefault: units == Units.Metric),
|
||||||
|
new SelectMenuOptionBuilder(
|
||||||
|
"Imperial",
|
||||||
|
"2",
|
||||||
|
isDefault: units == Units.Imperial),
|
||||||
|
new SelectMenuOptionBuilder(
|
||||||
|
"Kelvin",
|
||||||
|
"0",
|
||||||
|
isDefault: units == Units.Si)
|
||||||
|
],
|
||||||
|
id: RecipeUnitSelectMenu
|
||||||
|
)
|
||||||
|
])
|
||||||
|
.WithTextDisplay(instructions.ToString());
|
||||||
|
}
|
||||||
31
docs/guides/components_v2/samples/interactions.cs
Normal file
31
docs/guides/components_v2/samples/interactions.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
private async Task ClientOnInteractionCreatedAsync(SocketInteraction arg)
|
||||||
|
{
|
||||||
|
switch (arg)
|
||||||
|
{
|
||||||
|
case SocketMessageComponent component:
|
||||||
|
switch (component.Data.CustomId)
|
||||||
|
{
|
||||||
|
// Non dynamic cases ...
|
||||||
|
|
||||||
|
default:
|
||||||
|
var customId = component.Data.CustomId;
|
||||||
|
var lastPartStartIndex = customId.LastIndexOf('-');
|
||||||
|
|
||||||
|
if (lastPartStartIndex == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (customId[..lastPartStartIndex] == RecipesLookInsideButton) // "recipes-show-me-button"
|
||||||
|
await component.UpdateAsync(m => m.Components = BuildComponentUnsafe(_recipes.First(r => r.RecipeId == int.Parse(customId[(lastPartStartIndex + 1)..]))).Build()); // _recipes is a list of Recipe objects ; int.Parse({recipe.RecipeId}) (in this case it is 1)
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SocketModal modal:
|
||||||
|
// Interaction came from a modal
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
docs/guides/components_v2/samples/recipe-interactions.cs
Normal file
59
docs/guides/components_v2/samples/recipe-interactions.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
private async Task ClientOnInteractionCreatedAsync(SocketInteraction arg)
|
||||||
|
{
|
||||||
|
switch (arg)
|
||||||
|
{
|
||||||
|
case SocketMessageComponent component:
|
||||||
|
switch (component.Data.CustomId)
|
||||||
|
{
|
||||||
|
// SET SERVINGS BUTTON CLICKED
|
||||||
|
case RecipeServingsModal:
|
||||||
|
var servings = short.Parse(component.Message.Components.FindComponentById<TextDisplayComponent>(RecipeServingsDisplay).Content.Split(' ')[1]);
|
||||||
|
|
||||||
|
await component.RespondWithModalAsync(CreateServingsModal(servings).Build());
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ITEM IN COMBOXBOX CHANGED
|
||||||
|
case RecipeUnitInput:
|
||||||
|
SelectMenuComponent selectedItem = component.Message.Components.FindComponentById<SelectMenuComponent>(RecipeUnitSelectMenu);
|
||||||
|
var unitValue = short.Parse(component.Data.Values.First());
|
||||||
|
var recipeName = component.Message.Components.FindComponentById<TextDisplayComponent>(RecipeNameDisplay).Content[2..];
|
||||||
|
Recipe recipe = _recipes.First(r => r.Name == recipeName);
|
||||||
|
Recipe recipe0 = recipe.Clone();
|
||||||
|
var unit = (Units)Enum.ToObject(typeof(Units), unitValue);
|
||||||
|
|
||||||
|
ComponentBuilderV2 newComponentContainer = BuildComponentUnsafe(recipe0, unit);
|
||||||
|
|
||||||
|
await component.UpdateAsync(m => m.Components = newComponentContainer.Build());
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Ununsed here
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// MODAL SUBMIT
|
||||||
|
case SocketModal modal:
|
||||||
|
if (modal.Data.CustomId == RecipeServingsButton)
|
||||||
|
{
|
||||||
|
var success = short.TryParse(modal.Data.Components.First(c => c.CustomId == RecipeServingsInput).Value, out var servings);
|
||||||
|
|
||||||
|
if (!success || servings <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Recipe recipe = _recipes.First(r => r.Name == modal.Message.Components.FindComponentById<TextDisplayComponent>(RecipeNameDisplay).Content[2..]);
|
||||||
|
Recipe? recipe0 = recipe.Clone();
|
||||||
|
|
||||||
|
recipe0.ChangeServings(servings, true);
|
||||||
|
|
||||||
|
ComponentBuilderV2 newComponentContainer = BuildComponentUnsafe(recipe0);
|
||||||
|
|
||||||
|
await modal.UpdateAsync(m => m.Components = newComponentContainer.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
docs/guides/components_v2/samples/recipe-servings-modal.cs
Normal file
15
docs/guides/components_v2/samples/recipe-servings-modal.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
private static ModalBuilder CreateServingsModal(short servings)
|
||||||
|
{
|
||||||
|
TextInputBuilder? textInput = new TextInputBuilder()
|
||||||
|
.WithCustomId(RecipeServingsInput)
|
||||||
|
.WithLabel("Servings")
|
||||||
|
.WithValue(servings.ToString())
|
||||||
|
.WithMinLength(1)
|
||||||
|
.WithMaxLength(3)
|
||||||
|
.WithStyle(TextInputStyle.Short);
|
||||||
|
|
||||||
|
return new ModalBuilder()
|
||||||
|
.WithCustomId(RecipeServingsButton)
|
||||||
|
.WithTitle("Set Servings")
|
||||||
|
.AddTextInput(textInput);
|
||||||
|
}
|
||||||
14
docs/guides/components_v2/samples/recipes-command.cs
Normal file
14
docs/guides/components_v2/samples/recipes-command.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[SlashCommand("recipes", "Gets all recipes")]
|
||||||
|
public async Task GetRecipesAsync()
|
||||||
|
{
|
||||||
|
MessageComponent? embed = (await recipeService.GetRecipesComponentAsync())?.Build();
|
||||||
|
|
||||||
|
if (embed is null)
|
||||||
|
{
|
||||||
|
await RespondAsync($"No recipes found.", ephemeral: true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await RespondAsync(components: embed);
|
||||||
|
}
|
||||||
32
docs/guides/components_v2/samples/recipes-component.cs
Normal file
32
docs/guides/components_v2/samples/recipes-component.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
private async Task<ComponentBuilderV2> BuildComponentsUnsafeAsync()
|
||||||
|
{
|
||||||
|
if (!_recipes.Any()) // _recipes is simply a list of recipe objects
|
||||||
|
{
|
||||||
|
return new ComponentBuilderV2()
|
||||||
|
.WithTextDisplay(
|
||||||
|
"""
|
||||||
|
# No recipes found
|
||||||
|
You should consider adding some.
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new ComponentBuilderV2();
|
||||||
|
Emote? emote = await _clientProvider.Client.GetApplicationEmoteAsync(1393996479357517925);
|
||||||
|
|
||||||
|
foreach (Recipe recipe in _recipes)
|
||||||
|
{
|
||||||
|
var buttonBuilder = new ButtonBuilder("Look inside", $"{RecipesLookInsideButton}-{recipe.RecipeId}"); // RecipesLookInsideButton is a constant string
|
||||||
|
|
||||||
|
if (emote is not null)
|
||||||
|
buttonBuilder.WithEmote(emote);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.WithTextDisplay($"# {recipe.Name}")
|
||||||
|
.WithMediaGallery(["https://cdn.discordapp.com/attachments/964253122547552349/1336440069892083712/7Q3S.gif?ex=67a3d04e&is=67a27ece&hm=059c9d28466f43a50c4b450ca26fc01298a2080356421d8524384bf67ea8f3ab&"])
|
||||||
|
.WithActionRow([
|
||||||
|
buttonBuilder
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
- name: Introduction
|
- name: Introduction
|
||||||
topicUid: Guides.Introduction
|
topicUid: Guides.Introduction
|
||||||
- name: V2 to V3 Guide
|
- name: Breakings
|
||||||
topicUid: Guides.V2V3Guide
|
items:
|
||||||
|
- name: V2 to V3 Guide
|
||||||
|
topicUid: Guides.Breakings.V2V3Guide
|
||||||
|
- name: V3.18
|
||||||
|
topicUid: Guides.Breakings.V3_18
|
||||||
- name: Getting Started
|
- name: Getting Started
|
||||||
items:
|
items:
|
||||||
- name: Installation
|
- name: Installation
|
||||||
@@ -102,6 +106,14 @@
|
|||||||
topicUid: Guides.MessageComponents.TextInputs
|
topicUid: Guides.MessageComponents.TextInputs
|
||||||
- name: Advanced Concepts
|
- name: Advanced Concepts
|
||||||
topicUid: Guides.MessageComponents.Advanced
|
topicUid: Guides.MessageComponents.Advanced
|
||||||
|
- name: Components V2
|
||||||
|
items:
|
||||||
|
- name: Introduction
|
||||||
|
topicUid: Guides.ComponentsV2.Intro
|
||||||
|
- name: Advanced
|
||||||
|
topicUid: Guides.ComponentsV2.Advanced
|
||||||
|
- name: Interacting with Components
|
||||||
|
topicUid: Guides.ComponentsV2.Interaction
|
||||||
- name: Modal Basics
|
- name: Modal Basics
|
||||||
items:
|
items:
|
||||||
- name: Introduction
|
- name: Introduction
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ interface with the [Discord API](https://discord.com/).
|
|||||||
If this is your first time using Discord.Net, you should refer to the
|
If this is your first time using Discord.Net, you should refer to the
|
||||||
[Intro](xref:Guides.Introduction) for tutorials.
|
[Intro](xref:Guides.Introduction) for tutorials.
|
||||||
|
|
||||||
If you're coming from Discord.Net V2, you should refer to the [V2 -> V3](xref:Guides.V2V3Guide) guides.
|
If you're coming from Discord.Net V2, you should refer to the [V2 -> V3](xref:Guides.Breakings.V2V3Guide) guides.
|
||||||
|
|
||||||
More experienced users might want to refer to the
|
More experienced users might want to refer to the
|
||||||
[API Documentation](xref:API.Docs) for a breakdown of the individual
|
[API Documentation](xref:API.Docs) for a breakdown of the individual
|
||||||
|
|||||||
Reference in New Issue
Block a user