From 7efca819daa155b36faa181c1690607fde6e70e6 Mon Sep 17 00:00:00 2001 From: Filip Hoffmann Date: Thu, 29 May 2025 22:09:35 +0200 Subject: [PATCH] components rewritten --- src/flybycord/message/component.gleam | 314 +++--------------- .../message/component/action_row.gleam | 42 ++- .../message/component/channel_select.gleam | 61 ++++ .../message/component/container.gleam | 79 +++++ src/flybycord/message/component/file.gleam | 19 ++ .../message/component/media_gallery.gleam | 34 ++ src/flybycord/message/component/section.gleam | 78 +++++ .../component/select/default_value.gleam | 34 -- .../message/component/separator.gleam | 18 + .../message/component/text_display.gleam | 17 + .../message/component/thumbnail.gleam | 2 + .../component/unfurled_media_item.gleam | 40 +++ 12 files changed, 433 insertions(+), 305 deletions(-) create mode 100644 src/flybycord/message/component/channel_select.gleam create mode 100644 src/flybycord/message/component/container.gleam create mode 100644 src/flybycord/message/component/file.gleam create mode 100644 src/flybycord/message/component/media_gallery.gleam create mode 100644 src/flybycord/message/component/section.gleam delete mode 100644 src/flybycord/message/component/select/default_value.gleam create mode 100644 src/flybycord/message/component/text_display.gleam create mode 100644 src/flybycord/message/component/thumbnail.gleam create mode 100644 src/flybycord/message/component/unfurled_media_item.gleam diff --git a/src/flybycord/message/component.gleam b/src/flybycord/message/component.gleam index c77f040..85ad02c 100644 --- a/src/flybycord/message/component.gleam +++ b/src/flybycord/message/component.gleam @@ -1,16 +1,19 @@ -import flybycord/channel -import flybycord/emoji.{type Emoji} import flybycord/message/component/action_row.{type ActionRow} import flybycord/message/component/button.{type Button} +import flybycord/message/component/channel_select.{type ChannelSelect} +import flybycord/message/component/container.{type Container} +import flybycord/message/component/file.{type File} +import flybycord/message/component/media_gallery.{type MediaGallery} import flybycord/message/component/mentionable_select.{type MentionableSelect} import flybycord/message/component/role_select.{type RoleSelect} -import flybycord/message/component/select/default_value.{type DefaultValue} -import flybycord/message/component/separator +import flybycord/message/component/section.{type Section} +import flybycord/message/component/separator.{type Separator} import flybycord/message/component/string_select.{type StringSelect} +import flybycord/message/component/text_display.{type TextDisplay} import flybycord/message/component/text_input.{type TextInput} import flybycord/message/component/user_select.{type UserSelect} import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/option.{None} // TYPES ----------------------------------------------------------------------- @@ -22,65 +25,13 @@ pub type Component { UserSelect(UserSelect) RoleSelect(RoleSelect) MentionableSelect(MentionableSelect) - ChannelSelect( - id: Option(Int), - custom_id: String, - channel_types: Option(List(channel.Type)), - placeholder: Option(String), - default_values: Option(List(DefaultValue)), - min_values: Option(Int), - max_values: Option(Int), - is_disabled: Option(Bool), - ) - Section(id: Option(Int), components: List(Component), accessory: Component) - TextDisplay(id: Option(Int), content: String) - Thumbnail( - id: Option(Int), - media: UnfurledMediaItem, - description: Option(String), - is_spoiler: Option(Bool), - ) - MediaGallery(id: Option(Int), items: List(MediaGalleryItem)) - File(id: Option(Int), file: UnfurledMediaItem, is_spoiler: Option(Bool)) - Separator( - id: Option(Int), - is_divider: Option(Bool), - spacing: Option(separator.Spacing), - ) - Container( - id: Option(Int), - components: List(Component), - accent_color: Option(Int), - is_spoiler: Option(Bool), - ) -} - -pub type SelectOption { - SelectOption( - label: String, - value: String, - description: Option(String), - emoji: Option(Emoji), - is_default: Option(Bool), - ) -} - -pub type MediaGalleryItem { - MediaGalleryItem( - media: UnfurledMediaItem, - description: Option(String), - is_spoiler: Option(Bool), - ) -} - -pub type UnfurledMediaItem { - UnfurledMediaItem( - url: String, - proxy_url: Option(String), - height: Option(Int), - width: Option(Int), - content_type: Option(String), - ) + ChannelSelect(ChannelSelect) + Section(Section) + TextDisplay(TextDisplay) + MediaGallery(MediaGallery) + File(File) + Separator(Separator) + Container(Container) } // DECODERS -------------------------------------------------------------------- @@ -117,211 +68,34 @@ pub fn decoder() -> decode.Decoder(Component) { use mentionable_select <- decode.then(mentionable_select.decoder()) decode.success(MentionableSelect(mentionable_select)) } - 8 -> channel_select_decoder() - 9 -> section_decoder() - 10 -> text_display_decoder() - 11 -> thumbnail_decoder() - 12 -> media_gallery_decoder() - 13 -> file_decoder() - 14 -> separator_decoder() - 17 -> container_decoder() - _ -> decode.failure(todo, "Component") + 8 -> { + use channel_select <- decode.then(channel_select.decoder()) + decode.success(ChannelSelect(channel_select)) + } + 9 -> { + use section <- decode.then(section.decoder()) + decode.success(Section(section)) + } + 10 -> { + use text_display <- decode.then(text_display.decoder()) + decode.success(TextDisplay(text_display)) + } + 12 -> { + use media_gallery <- decode.then(media_gallery.decoder()) + decode.success(MediaGallery(media_gallery)) + } + 13 -> { + use file <- decode.then(file.decoder()) + decode.success(File(file)) + } + 14 -> { + use separator <- decode.then(separator.decoder()) + decode.success(Separator(separator)) + } + 17 -> { + use container <- decode.then(container.decoder()) + decode.success(Container(container)) + } + _ -> decode.failure(ActionRow(action_row.ActionRow(None, [])), "Component") } } - -fn channel_select_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use custom_id <- decode.field("custom_id", decode.string) - use channel_types <- decode.optional_field( - "channel_types", - None, - decode.optional(decode.list(channel.type_decoder())), - ) - use placeholder <- decode.optional_field( - "placeholder", - None, - decode.optional(decode.string), - ) - use default_values <- decode.optional_field( - "default_values", - None, - decode.optional(decode.list(default_value.decoder())), - ) - use min_values <- decode.optional_field( - "min_values", - None, - decode.optional(decode.int), - ) - use max_values <- decode.optional_field( - "max_values", - None, - decode.optional(decode.int), - ) - use is_disabled <- decode.optional_field( - "disabled", - None, - decode.optional(decode.bool), - ) - - decode.success(ChannelSelect( - id:, - custom_id:, - channel_types:, - placeholder:, - default_values:, - min_values:, - max_values:, - is_disabled:, - )) -} - -fn section_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use components <- decode.field("components", decode.list(decoder())) - use accessory <- decode.field("accessory", decoder()) - - decode.success(Section(id:, components:, accessory:)) -} - -fn text_display_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use content <- decode.field("content", decode.string) - - decode.success(TextDisplay(id:, content:)) -} - -fn thumbnail_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use media <- decode.field("media", unfurled_media_item_decoder()) - use description <- decode.optional_field( - "description", - None, - decode.optional(decode.string), - ) - use is_spoiler <- decode.optional_field( - "spoiler", - None, - decode.optional(decode.bool), - ) - - decode.success(Thumbnail(id:, media:, description:, is_spoiler:)) -} - -fn media_gallery_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use items <- decode.field("items", decode.list(media_gallery_item_decoder())) - - decode.success(MediaGallery(id:, items:)) -} - -fn file_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use file <- decode.field("file", unfurled_media_item_decoder()) - use is_spoiler <- decode.optional_field( - "spoiler", - None, - decode.optional(decode.bool), - ) - - decode.success(File(id:, file:, is_spoiler:)) -} - -fn separator_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use is_divider <- decode.optional_field( - "divider", - None, - decode.optional(decode.bool), - ) - use spacing <- decode.optional_field( - "spacing", - None, - decode.optional(separator.spacing_decoder()), - ) - - decode.success(Separator(id:, is_divider:, spacing:)) -} - -fn container_decoder() -> decode.Decoder(Component) { - use id <- decode.optional_field("id", None, decode.optional(decode.int)) - use components <- decode.field("components", decode.list(decoder())) - use accent_color <- decode.optional_field( - "accent_color", - None, - decode.optional(decode.int), - ) - use is_spoiler <- decode.optional_field( - "spoiler", - None, - decode.optional(decode.bool), - ) - - decode.success(Container(id:, components:, accent_color:, is_spoiler:)) -} - -@internal -pub fn select_option_decoder() -> decode.Decoder(SelectOption) { - use label <- decode.field("label", decode.string) - use value <- decode.field("value", decode.string) - use description <- decode.optional_field( - "description", - None, - decode.optional(decode.string), - ) - use emoji <- decode.optional_field( - "emoji", - None, - decode.optional(emoji.decoder()), - ) - use is_default <- decode.optional_field( - "default", - None, - decode.optional(decode.bool), - ) - - decode.success(SelectOption(label:, value:, description:, emoji:, is_default:)) -} - -@internal -pub fn unfurled_media_item_decoder() -> decode.Decoder(UnfurledMediaItem) { - use url <- decode.field("url", decode.string) - use proxy_url <- decode.optional_field( - "proxy_url", - None, - decode.optional(decode.string), - ) - use height <- decode.optional_field( - "height", - None, - decode.optional(decode.int), - ) - use width <- decode.optional_field("width", None, decode.optional(decode.int)) - use content_type <- decode.optional_field( - "content_type", - None, - decode.optional(decode.string), - ) - decode.success(UnfurledMediaItem( - url:, - proxy_url:, - height:, - width:, - content_type:, - )) -} - -@internal -pub fn media_gallery_item_decoder() -> decode.Decoder(MediaGalleryItem) { - use media <- decode.field("media", unfurled_media_item_decoder()) - use description <- decode.optional_field( - "description", - None, - decode.optional(decode.string), - ) - use is_spoiler <- decode.optional_field( - "spoiler", - None, - decode.optional(decode.bool), - ) - decode.success(MediaGalleryItem(media:, description:, is_spoiler:)) -} diff --git a/src/flybycord/message/component/action_row.gleam b/src/flybycord/message/component/action_row.gleam index 4b8529e..fefec0e 100644 --- a/src/flybycord/message/component/action_row.gleam +++ b/src/flybycord/message/component/action_row.gleam @@ -1,6 +1,10 @@ import flybycord/message/component/button.{type Button} +import flybycord/message/component/channel_select.{type ChannelSelect} +import flybycord/message/component/mentionable_select.{type MentionableSelect} +import flybycord/message/component/role_select.{type RoleSelect} import flybycord/message/component/string_select.{type StringSelect} import flybycord/message/component/text_input.{type TextInput} +import flybycord/message/component/user_select.{type UserSelect} import gleam/dynamic/decode import gleam/option.{type Option, None} @@ -14,6 +18,10 @@ pub type Component { Button(Button) TextInput(TextInput) StringSelect(StringSelect) + UserSelect(UserSelect) + RoleSelect(RoleSelect) + MentionableSelect(MentionableSelect) + ChannelSelect(ChannelSelect) } // DECODERS -------------------------------------------------------------------- @@ -30,6 +38,38 @@ pub fn decoder() -> decode.Decoder(ActionRow) { pub fn component_decoder() -> decode.Decoder(Component) { use type_ <- decode.field("type", decode.int) case type_ { - _ -> todo + 2 -> { + use button <- decode.then(button.decoder()) + decode.success(Button(button)) + } + 3 -> { + use string_select <- decode.then(string_select.decoder()) + decode.success(StringSelect(string_select)) + } + 4 -> { + use text_input <- decode.then(text_input.decoder()) + decode.success(TextInput(text_input)) + } + 5 -> { + use user_select <- decode.then(user_select.decoder()) + decode.success(UserSelect(user_select)) + } + 6 -> { + use role_select <- decode.then(role_select.decoder()) + decode.success(RoleSelect(role_select)) + } + 7 -> { + use mentionable_select <- decode.then(mentionable_select.decoder()) + decode.success(MentionableSelect(mentionable_select)) + } + 8 -> { + use channel_select <- decode.then(channel_select.decoder()) + decode.success(ChannelSelect(channel_select)) + } + _ -> + decode.failure( + Button(button.Regular(None, button.Primary, None, None, "", False)), + "Component", + ) } } diff --git a/src/flybycord/message/component/channel_select.gleam b/src/flybycord/message/component/channel_select.gleam new file mode 100644 index 0000000..e6fb282 --- /dev/null +++ b/src/flybycord/message/component/channel_select.gleam @@ -0,0 +1,61 @@ +import flybycord/channel +import gleam/dynamic/decode +import gleam/option.{type Option, None} + +pub type ChannelSelect { + ChannelSelect( + id: Option(Int), + custom_id: String, + channel_types: Option(List(channel.Type)), + placeholder: Option(String), + default_values: Option(List(DefaultValue)), + min_values: Int, + max_values: Int, + is_disabled: Bool, + ) +} + +pub type DefaultValue { + DefaultValue(id: String) +} + +@internal +pub fn decoder() -> decode.Decoder(ChannelSelect) { + use id <- decode.optional_field("id", None, decode.optional(decode.int)) + use custom_id <- decode.field("custom_id", decode.string) + use channel_types <- decode.optional_field( + "channel_types", + None, + decode.optional(decode.list(channel.type_decoder())), + ) + use placeholder <- decode.optional_field( + "placeholder", + None, + decode.optional(decode.string), + ) + use default_values <- decode.optional_field( + "default_values", + None, + decode.optional(decode.list(default_value_decoder())), + ) + use min_values <- decode.optional_field("min_values", 1, decode.int) + use max_values <- decode.optional_field("max_values", 1, decode.int) + use is_disabled <- decode.optional_field("disabled", False, decode.bool) + + decode.success(ChannelSelect( + id:, + custom_id:, + channel_types:, + placeholder:, + default_values:, + min_values:, + max_values:, + is_disabled:, + )) +} + +@internal +pub fn default_value_decoder() -> decode.Decoder(DefaultValue) { + use id <- decode.field("id", decode.string) + decode.success(DefaultValue(id:)) +} diff --git a/src/flybycord/message/component/container.gleam b/src/flybycord/message/component/container.gleam new file mode 100644 index 0000000..c1fd452 --- /dev/null +++ b/src/flybycord/message/component/container.gleam @@ -0,0 +1,79 @@ +import flybycord/message/component/action_row.{type ActionRow} +import flybycord/message/component/file.{type File} +import flybycord/message/component/media_gallery.{type MediaGallery} +import flybycord/message/component/section.{type Section} +import flybycord/message/component/separator.{type Separator} +import flybycord/message/component/text_display.{type TextDisplay} +import gleam/dynamic/decode +import gleam/option.{type Option, None} + +// TYPES ----------------------------------------------------------------------- + +pub type Container { + Container( + id: Option(Int), + components: List(Component), + accent_color: Option(Int), + is_spoiler: Bool, + ) +} + +pub type Component { + ActionRow(ActionRow) + TextDisplay(TextDisplay) + Section(Section) + MediaGallery(MediaGallery) + Separator(Separator) + File(File) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn decoder() -> decode.Decoder(Container) { + use id <- decode.optional_field("id", None, decode.optional(decode.int)) + use components <- decode.field("components", decode.list(component_decoder())) + use accent_color <- decode.optional_field( + "accent_color", + None, + decode.optional(decode.int), + ) + use is_spoiler <- decode.optional_field("spoiler", False, decode.bool) + decode.success(Container(id:, components:, accent_color:, is_spoiler:)) +} + +@internal +pub fn component_decoder() -> decode.Decoder(Component) { + use type_ <- decode.field("type", decode.int) + case type_ { + 1 -> { + use action_row <- decode.then(action_row.decoder()) + decode.success(ActionRow(action_row)) + } + 9 -> { + use section <- decode.then(section.decoder()) + decode.success(Section(section)) + } + 10 -> { + use text_display <- decode.then(text_display.decoder()) + decode.success(TextDisplay(text_display)) + } + 12 -> { + use media_gallery <- decode.then(media_gallery.decoder()) + decode.success(MediaGallery(media_gallery)) + } + 13 -> { + use file <- decode.then(file.decoder()) + decode.success(File(file)) + } + 14 -> { + use separator <- decode.then(separator.decoder()) + decode.success(Separator(separator)) + } + _ -> + decode.failure( + TextDisplay(text_display.TextDisplay(None, "")), + "Component", + ) + } +} diff --git a/src/flybycord/message/component/file.gleam b/src/flybycord/message/component/file.gleam new file mode 100644 index 0000000..79c94f5 --- /dev/null +++ b/src/flybycord/message/component/file.gleam @@ -0,0 +1,19 @@ +import flybycord/message/component/unfurled_media_item.{type UnfurledMediaItem} +import gleam/dynamic/decode +import gleam/option.{type Option, None} + +// TYPES ----------------------------------------------------------------------- + +pub type File { + File(id: Option(Int), file: UnfurledMediaItem, is_spoiler: Bool) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn decoder() -> decode.Decoder(File) { + use id <- decode.optional_field("id", None, decode.optional(decode.int)) + use file <- decode.field("file", unfurled_media_item.decoder()) + use is_spoiler <- decode.optional_field("spoiler", False, decode.bool) + decode.success(File(id:, file:, is_spoiler:)) +} diff --git a/src/flybycord/message/component/media_gallery.gleam b/src/flybycord/message/component/media_gallery.gleam new file mode 100644 index 0000000..2059f1f --- /dev/null +++ b/src/flybycord/message/component/media_gallery.gleam @@ -0,0 +1,34 @@ +import flybycord/message/component/unfurled_media_item.{type UnfurledMediaItem} +import gleam/dynamic/decode +import gleam/option.{type Option, None} + +// TYPES ----------------------------------------------------------------------- + +pub type MediaGallery { + MediaGallery(id: Option(Int), items: List(Item)) +} + +pub type Item { + Item(media: UnfurledMediaItem, description: Option(String), is_spoiler: Bool) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn decoder() -> decode.Decoder(MediaGallery) { + use id <- decode.field("id", decode.optional(decode.int)) + use items <- decode.field("items", decode.list(item_decoder())) + decode.success(MediaGallery(id:, items:)) +} + +@internal +pub fn item_decoder() -> decode.Decoder(Item) { + use media <- decode.field("media", unfurled_media_item.decoder()) + use description <- decode.optional_field( + "description", + None, + decode.optional(decode.string), + ) + use is_spoiler <- decode.optional_field("spoiler", False, decode.bool) + decode.success(Item(media:, description:, is_spoiler:)) +} diff --git a/src/flybycord/message/component/section.gleam b/src/flybycord/message/component/section.gleam new file mode 100644 index 0000000..add9c24 --- /dev/null +++ b/src/flybycord/message/component/section.gleam @@ -0,0 +1,78 @@ +import flybycord/message/component/button.{type Button} +import flybycord/message/component/text_display.{type TextDisplay} +import flybycord/message/component/unfurled_media_item.{type UnfurledMediaItem} +import gleam/dynamic/decode +import gleam/option.{type Option, None} + +// TYPES ----------------------------------------------------------------------- + +pub type Section { + Section(id: Option(Int), components: List(Component), accessory: Accessory) +} + +pub type Component { + TextDisplay(TextDisplay) +} + +pub type Accessory { + Thumbnail( + id: Option(Int), + media: UnfurledMediaItem, + description: Option(String), + is_spoiler: Bool, + ) + Button(Button) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn decoder() -> decode.Decoder(Section) { + use id <- decode.optional_field("id", None, decode.optional(decode.int)) + use components <- decode.field("components", decode.list(component_decoder())) + use accessory <- decode.field("accessory", accessory_decoder()) + decode.success(Section(id:, components:, accessory:)) +} + +@internal +pub fn component_decoder() -> decode.Decoder(Component) { + use type_ <- decode.field("type", decode.int) + case type_ { + 10 -> { + use text_display <- decode.then(text_display.decoder()) + decode.success(TextDisplay(text_display)) + } + _ -> + decode.failure( + TextDisplay(text_display.TextDisplay(None, "")), + "Component", + ) + } +} + +@internal +pub fn accessory_decoder() { + use type_ <- decode.field("type", decode.int) + case type_ { + 2 -> { + use button <- decode.then(button.decoder()) + decode.success(Button(button)) + } + 11 -> { + use id <- decode.optional_field("id", None, decode.optional(decode.int)) + use media <- decode.field("media", unfurled_media_item.decoder()) + use description <- decode.optional_field( + "description", + None, + decode.optional(decode.string), + ) + use is_spoiler <- decode.optional_field("spoiler", False, decode.bool) + decode.success(Thumbnail(id:, media:, description:, is_spoiler:)) + } + _ -> + decode.failure( + Button(button.Regular(None, button.Primary, None, None, "", False)), + "Accessory", + ) + } +} diff --git a/src/flybycord/message/component/select/default_value.gleam b/src/flybycord/message/component/select/default_value.gleam deleted file mode 100644 index 51c39ca..0000000 --- a/src/flybycord/message/component/select/default_value.gleam +++ /dev/null @@ -1,34 +0,0 @@ -import gleam/dynamic/decode - -// TYPES ----------------------------------------------------------------------- - -pub type DefaultValue { - DefaultValue(id: String, type_: Type) -} - -pub type Type { - User - Role - Channel -} - -// DECODERS -------------------------------------------------------------------- - -@internal -pub fn decoder() -> decode.Decoder(DefaultValue) { - use id <- decode.field("id", decode.string) - use type_ <- decode.field("type", type_decoder()) - - decode.success(DefaultValue(id:, type_:)) -} - -@internal -pub fn type_decoder() -> decode.Decoder(Type) { - use variant <- decode.then(decode.string) - case variant { - "user" -> decode.success(User) - "role" -> decode.success(Role) - "channel" -> decode.success(Channel) - _ -> decode.failure(User, "Type") - } -} diff --git a/src/flybycord/message/component/separator.gleam b/src/flybycord/message/component/separator.gleam index 102ec9a..274888d 100644 --- a/src/flybycord/message/component/separator.gleam +++ b/src/flybycord/message/component/separator.gleam @@ -1,7 +1,12 @@ import gleam/dynamic/decode +import gleam/option.{type Option, None} // TYPES ----------------------------------------------------------------------- +pub type Separator { + Separator(id: Option(Int), show_divider: Bool, spacing: Spacing) +} + pub type Spacing { SmallPadding LargePadding @@ -9,6 +14,19 @@ pub type Spacing { // DECODERS -------------------------------------------------------------------- +@internal +pub fn decoder() -> decode.Decoder(Separator) { + use id <- decode.optional_field("id", None, decode.optional(decode.int)) + use show_divider <- decode.optional_field("divider", True, decode.bool) + use spacing <- decode.optional_field( + "spacing", + SmallPadding, + spacing_decoder(), + ) + + decode.success(Separator(id:, show_divider:, spacing:)) +} + @internal pub fn spacing_decoder() -> decode.Decoder(Spacing) { use variant <- decode.then(decode.int) diff --git a/src/flybycord/message/component/text_display.gleam b/src/flybycord/message/component/text_display.gleam new file mode 100644 index 0000000..7fa5cde --- /dev/null +++ b/src/flybycord/message/component/text_display.gleam @@ -0,0 +1,17 @@ +import gleam/dynamic/decode +import gleam/option.{type Option} + +// TYPES ----------------------------------------------------------------------- + +pub type TextDisplay { + TextDisplay(id: Option(Int), content: String) +} + +// DECODERS -------------------------------------------------------------------- + +@internal +pub fn decoder() -> decode.Decoder(TextDisplay) { + use id <- decode.field("id", decode.optional(decode.int)) + use content <- decode.field("content", decode.string) + decode.success(TextDisplay(id:, content:)) +} diff --git a/src/flybycord/message/component/thumbnail.gleam b/src/flybycord/message/component/thumbnail.gleam new file mode 100644 index 0000000..139597f --- /dev/null +++ b/src/flybycord/message/component/thumbnail.gleam @@ -0,0 +1,2 @@ + + diff --git a/src/flybycord/message/component/unfurled_media_item.gleam b/src/flybycord/message/component/unfurled_media_item.gleam new file mode 100644 index 0000000..f745bd3 --- /dev/null +++ b/src/flybycord/message/component/unfurled_media_item.gleam @@ -0,0 +1,40 @@ +import gleam/dynamic/decode +import gleam/option.{type Option, None} + +pub type UnfurledMediaItem { + UnfurledMediaItem( + url: String, + proxy_url: Option(String), + height: Option(Int), + width: Option(Int), + content_type: Option(String), + ) +} + +@internal +pub fn decoder() -> decode.Decoder(UnfurledMediaItem) { + use url <- decode.field("url", decode.string) + use proxy_url <- decode.optional_field( + "proxy_url", + None, + decode.optional(decode.string), + ) + use height <- decode.optional_field( + "height", + None, + decode.optional(decode.int), + ) + use width <- decode.optional_field("width", None, decode.optional(decode.int)) + use content_type <- decode.optional_field( + "content_type", + None, + decode.optional(decode.string), + ) + decode.success(UnfurledMediaItem( + url:, + proxy_url:, + height:, + width:, + content_type:, + )) +} -- 2.51.2