From 62edd8858fa24807e5b600078d61e1c13b7ad4e6 Mon Sep 17 00:00:00 2001 From: Filip Hoffmann Date: Sat, 28 Jun 2025 15:12:25 +0200 Subject: [PATCH] component encoders --- src/grom/message/component/action_row.gleam | 16 +++++-- src/grom/message/component/container.gleam | 43 ++++++++++++++++++- src/grom/message/component/file.gleam | 36 ++++++++++++++-- .../message/component/media_gallery.gleam | 38 +++++++++++++++- src/grom/message/component/section.gleam | 2 +- src/grom/message/component/separator.gleam | 33 +++++++++++++- .../component/unfurled_media_item.gleam | 18 ++++++++ 7 files changed, 176 insertions(+), 10 deletions(-) diff --git a/src/grom/message/component/action_row.gleam b/src/grom/message/component/action_row.gleam index c12ac0c..e3ef172 100644 --- a/src/grom/message/component/action_row.gleam +++ b/src/grom/message/component/action_row.gleam @@ -87,15 +87,25 @@ pub fn to_json(action_row: ActionRow) -> Json { None -> [] } - todo as "components" + let components = [ + #("components", json.array(action_row.components, component_to_json)), + ] - [type_, id] + [type_, id, components] |> list.flatten |> json.object } +@internal pub fn component_to_json(component: Component) -> Json { case component { - _ -> todo as "come back when the rest of components is done" + Button(button) -> button.to_json(button) + TextInput(text_input) -> text_input.to_json(text_input) + StringSelect(string_select) -> string_select.to_json(string_select) + UserSelect(user_select) -> user_select.to_json(user_select) + RoleSelect(role_select) -> role_select.to_json(role_select) + MentionableSelect(mentionable_select) -> + mentionable_select.to_json(mentionable_select) + ChannelSelect(channel_select) -> channel_select.to_json(channel_select) } } diff --git a/src/grom/message/component/container.gleam b/src/grom/message/component/container.gleam index 86208f4..16e238e 100644 --- a/src/grom/message/component/container.gleam +++ b/src/grom/message/component/container.gleam @@ -1,5 +1,7 @@ import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import grom/message/component/action_row.{type ActionRow} import grom/message/component/file.{type File} import grom/message/component/media_gallery.{type MediaGallery} @@ -77,3 +79,42 @@ pub fn component_decoder() -> decode.Decoder(Component) { ) } } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn to_json(container: Container) -> Json { + let type_ = [#("type", json.int(17))] + + let id = case container.id { + Some(id) -> [#("id", json.int(id))] + None -> [] + } + + let components = [ + #("components", json.array(container.components, component_to_json)), + ] + + let accent_color = case container.accent_color { + Some(color) -> [#("accent_color", json.int(color))] + None -> [] + } + + let is_spoiler = [#("spoiler", json.bool(container.is_spoiler))] + + [type_, id, components, accent_color, is_spoiler] + |> list.flatten + |> json.object +} + +@internal +pub fn component_to_json(component: Component) -> Json { + case component { + ActionRow(action_row) -> action_row.to_json(action_row) + TextDisplay(text_display) -> text_display.to_json(text_display) + Section(section) -> section.to_json(section) + MediaGallery(media_gallery) -> media_gallery.to_json(media_gallery) + Separator(separator) -> separator.to_json(separator) + File(file) -> file.to_json(file) + } +} diff --git a/src/grom/message/component/file.gleam b/src/grom/message/component/file.gleam index c5cff57..1f25055 100644 --- a/src/grom/message/component/file.gleam +++ b/src/grom/message/component/file.gleam @@ -1,11 +1,19 @@ import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import grom/message/component/unfurled_media_item.{type UnfurledMediaItem} // TYPES ----------------------------------------------------------------------- pub type File { - File(id: Option(Int), file: UnfurledMediaItem, is_spoiler: Bool) + File( + id: Option(Int), + file: UnfurledMediaItem, + is_spoiler: Bool, + name: String, + size: Int, + ) } // DECODERS -------------------------------------------------------------------- @@ -15,5 +23,27 @@ 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:)) + use name <- decode.field("name", decode.string) + use size <- decode.field("size", decode.int) + + decode.success(File(id:, file:, is_spoiler:, name:, size:)) +} + +// ENCODERS -------------------------------------------------------------------- + +pub fn to_json(file: File) -> Json { + let type_ = [#("type", json.int(13))] + + let id = case file.id { + Some(id) -> [#("id", json.int(id))] + None -> [] + } + + let file_ = [#("file", unfurled_media_item.to_json(file.file))] + + let is_spoiler = [#("spoiler", json.bool(file.is_spoiler))] + + [type_, id, file_, is_spoiler] + |> list.flatten + |> json.object } diff --git a/src/grom/message/component/media_gallery.gleam b/src/grom/message/component/media_gallery.gleam index e5aed01..6ddeeb1 100644 --- a/src/grom/message/component/media_gallery.gleam +++ b/src/grom/message/component/media_gallery.gleam @@ -1,5 +1,7 @@ import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} import grom/message/component/unfurled_media_item.{type UnfurledMediaItem} // TYPES ----------------------------------------------------------------------- @@ -32,3 +34,37 @@ pub fn item_decoder() -> decode.Decoder(Item) { use is_spoiler <- decode.optional_field("spoiler", False, decode.bool) decode.success(Item(media:, description:, is_spoiler:)) } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn to_json(media_gallery: MediaGallery) -> Json { + let type_ = [#("type", json.int(12))] + + let id = case media_gallery.id { + Some(id) -> [#("id", json.int(id))] + None -> [] + } + + let items = [#("items", json.array(media_gallery.items, item_to_json))] + + [type_, id, items] + |> list.flatten + |> json.object +} + +@internal +pub fn item_to_json(item: Item) -> Json { + let media = [#("media", unfurled_media_item.to_json(item.media))] + + let description = case item.description { + Some(description) -> [#("description", json.string(description))] + None -> [] + } + + let is_spoiler = [#("spoiler", json.bool(item.is_spoiler))] + + [media, description, is_spoiler] + |> list.flatten + |> json.object +} diff --git a/src/grom/message/component/section.gleam b/src/grom/message/component/section.gleam index 9facaa4..6706b3b 100644 --- a/src/grom/message/component/section.gleam +++ b/src/grom/message/component/section.gleam @@ -120,7 +120,7 @@ pub fn accessory_to_json(accessory: Accessory) -> Json { None -> [] } - let media = todo as "unfurled media item to json" + let media = [#("media", unfurled_media_item.to_json(accessory.media))] let description = case accessory.description { Some(description) -> [#("description", json.string(description))] diff --git a/src/grom/message/component/separator.gleam b/src/grom/message/component/separator.gleam index 274888d..4d31a97 100644 --- a/src/grom/message/component/separator.gleam +++ b/src/grom/message/component/separator.gleam @@ -1,5 +1,7 @@ import gleam/dynamic/decode -import gleam/option.{type Option, None} +import gleam/json.{type Json} +import gleam/list +import gleam/option.{type Option, None, Some} // TYPES ----------------------------------------------------------------------- @@ -36,3 +38,32 @@ pub fn spacing_decoder() -> decode.Decoder(Spacing) { _ -> decode.failure(SmallPadding, "Spacing") } } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn to_json(separator: Separator) -> Json { + let type_ = [#("type", json.int(14))] + + let id = case separator.id { + Some(id) -> [#("id", json.int(id))] + None -> [] + } + + let show_divider = [#("divider", json.bool(separator.show_divider))] + + let spacing = [#("spacing", spacing_to_json(separator.spacing))] + + [type_, id, show_divider, spacing] + |> list.flatten + |> json.object +} + +@internal +pub fn spacing_to_json(spacing: Spacing) -> Json { + case spacing { + SmallPadding -> 1 + LargePadding -> 2 + } + |> json.int +} diff --git a/src/grom/message/component/unfurled_media_item.gleam b/src/grom/message/component/unfurled_media_item.gleam index f745bd3..a0d67d9 100644 --- a/src/grom/message/component/unfurled_media_item.gleam +++ b/src/grom/message/component/unfurled_media_item.gleam @@ -1,6 +1,9 @@ import gleam/dynamic/decode +import gleam/json.{type Json} import gleam/option.{type Option, None} +// TYPES ----------------------------------------------------------------------- + pub type UnfurledMediaItem { UnfurledMediaItem( url: String, @@ -11,6 +14,8 @@ pub type UnfurledMediaItem { ) } +// DECODERS -------------------------------------------------------------------- + @internal pub fn decoder() -> decode.Decoder(UnfurledMediaItem) { use url <- decode.field("url", decode.string) @@ -38,3 +43,16 @@ pub fn decoder() -> decode.Decoder(UnfurledMediaItem) { content_type:, )) } + +// ENCODERS -------------------------------------------------------------------- + +@internal +pub fn to_json(unfurled_media_item: UnfurledMediaItem) -> Json { + json.object([#("url", json.string(unfurled_media_item.url))]) +} + +// PUBLIC API FUNCTIONS -------------------------------------------------------- + +pub fn new(url: String) -> UnfurledMediaItem { + UnfurledMediaItem(url, None, None, None, None) +} -- 2.51.2