glyde/component
Message components: action rows, buttons and select menus.
Two positions, two types. A Component is a top-level entry on a message:
today only an action row. A RowChild is what sits inside a row: a button,
a select menu. Discord answers 400 to a row nested in a row and to a button
at top level, and neither is a value that can be built here.
Types 1, 2, 3, 5, 6, 7 and 8 are modelled. Everything else is dropped from the list on decode, so another bot’s Components V2 message does not sink your MESSAGE_CREATE.
Types
A link button with a custom_id is a 400, so each kind carries only the
fields its style allows. Styles 5 and 6 are the link and premium buttons.
pub type ButtonKind {
ActionButton(
custom_id: String,
style: ButtonStyle,
label: ButtonLabel,
)
LinkButton(url: String, label: ButtonLabel)
PremiumButton(sku_id: id.Id(id.Sku))
}
Constructors
-
ActionButton( custom_id: String, style: ButtonStyle, label: ButtonLabel, ) -
LinkButton(url: String, label: ButtonLabel) -
Discord supplies the label from the SKU, so this one takes none.
Discord needs a button to say something, so “no label and no emoji” is not a state to have. Text is max 80 characters.
pub type ButtonLabel {
Text(String)
Icon(emoji.Emoji)
TextAndIcon(String, emoji.Emoji)
}
Constructors
-
Text(String) -
Icon(emoji.Emoji) -
TextAndIcon(String, emoji.Emoji)
A style this build has no name for decodes as None, so an action button
carrying one is dropped from its row.
pub type ButtonStyle {
PrimaryButton
SecondaryButton
SuccessButton
DangerButton
}
Constructors
-
PrimaryButton -
SecondaryButton -
SuccessButton -
DangerButton
A top-level entry in a message’s components. A component type this build
does not model is dropped from the list on decode.
pub type Component {
ActionRow(id: option.Option(Int), components: List(RowChild))
}
Constructors
-
ActionRow(id: option.Option(Int), components: List(RowChild))Type 1. Holds up to five buttons, or exactly one select menu.
Arguments
- id
-
Discord’s per-component sequence number, not a snowflake.
Discord’s type number, the one field both positions carry. Types 9 to 23
are Components V2, unmodelled, and a value this build has no name for
decodes as None.
pub type ComponentType {
ActionRowType
ButtonType
StringSelectType
TextInputType
UserSelectType
RoleSelectType
MentionableSelectType
ChannelSelectType
}
Constructors
-
ActionRowType -
ButtonType -
StringSelectType -
TextInputTypeA modal’s text input, one per action row.
-
UserSelectType -
RoleSelectType -
MentionableSelectType -
ChannelSelectType
What an action row holds. Never a row: Discord answers 400 to a nested one, and never a Components V2 element, which is top level. A child type this build does not model is dropped from the row on decode.
pub type RowChild {
Button(
id: option.Option(Int),
kind: ButtonKind,
disabled: Bool,
)
SelectMenu(
id: option.Option(Int),
custom_id: String,
kind: SelectKind,
placeholder: option.Option(String),
min_values: option.Option(Int),
max_values: option.Option(Int),
disabled: Bool,
)
}
Constructors
-
Button(id: option.Option(Int), kind: ButtonKind, disabled: Bool)Type 2. What it says is in
kind: a premium button takes its label from the SKU, and the other two need one of their own. -
SelectMenu( id: option.Option(Int), custom_id: String, kind: SelectKind, placeholder: option.Option(String), min_values: option.Option(Int), max_values: option.Option(Int), disabled: Bool, )Types 3, 5, 6, 7 and 8.
kindis the discriminator.Arguments
- custom_id
-
1 to 100 characters, chosen by you.
pub type SelectKind {
StringSelect(options: List(SelectOption))
UserSelect(default_users: List(id.Id(id.User)))
RoleSelect(default_roles: List(id.Id(id.Role)))
MentionableSelect(default_values: List(MentionableDefault))
ChannelSelect(
channel_types: List(channel.ChannelType),
default_channels: List(id.Id(id.Channel)),
)
}
Constructors
-
StringSelect(options: List(SelectOption))Type 3. Carries 1 to 25 options.
-
Type 5.
-
Type 6.
-
MentionableSelect(default_values: List(MentionableDefault))Type 7. Resolves to users and roles together.
-
ChannelSelect( channel_types: List(channel.ChannelType), default_channels: List(id.Id(id.Channel)), )Type 8.
Arguments
- channel_types
-
Empty means every channel type is offered.
pub type SelectOption {
SelectOption(
label: String,
value: String,
description: option.Option(String),
emoji: option.Option(emoji.Emoji),
default: Bool,
)
}
Constructors
-
SelectOption( label: String, value: String, description: option.Option(String), emoji: option.Option(emoji.Emoji), default: Bool, )Arguments
- label
-
Max 100 characters, shown to the user.
- value
-
Max 100 characters, sent back to you on click.
Values
pub fn button(
custom_id custom_id: String,
label label: String,
) -> RowChild
PrimaryButton is our default, not Discord’s: the API requires a style and
has none of its own. Change it with a record update.
pub fn button_style_decoder() -> decode.Decoder(
option.Option(ButtonStyle),
)
pub fn button_style_from_int(
value: Int,
) -> option.Option(ButtonStyle)
pub fn button_style_to_int(value: ButtonStyle) -> Int
pub fn button_style_to_json(value: ButtonStyle) -> json.Json
pub fn component_type(component: Component) -> ComponentType
pub fn component_type_from_int(
value: Int,
) -> option.Option(ComponentType)
pub fn component_type_to_int(value: ComponentType) -> Int
pub fn decoder() -> decode.Decoder(option.Option(Component))
A top-level component that is not an action row is dropped: None here,
filtered by wire.known_list_field at the call site. A button at top
level is a 400 on the way back out, so it is not decoded into one either.
pub fn link_button(
url url: String,
label label: String,
) -> RowChild
Opens a url, and sends no interaction.
pub fn option(
label label: String,
value value: String,
) -> SelectOption
pub fn premium_button(sku_id: id.Id(id.Sku)) -> RowChild
A premium upsell. Discord supplies the label from the SKU.
pub fn row_child_decoder() -> decode.Decoder(
option.Option(RowChild),
)
A child that is not a button or a select menu is dropped from its row.
That covers a text input, a nested row, and a button whose fields cannot
build a ButtonKind this side would send back.
pub fn row_child_type(child: RowChild) -> ComponentType
pub fn rows(children: List(RowChild)) -> List(Component)
Pack loose children into action rows: five buttons to a row, a whole row per select menu, order preserved. A row and a Components V2 element are top level, so they are not inputs here: append them to what this returns.
pub fn select(
custom_id custom_id: String,
kind kind: SelectKind,
) -> RowChild
pub fn select_option_decoder() -> decode.Decoder(SelectOption)
pub fn select_option_to_json(item: SelectOption) -> json.Json