small SPA gleam experiment to fetch and render a single bsky post
at main 157 lines 3.7 kB view raw
1import gleam/dynamic/decode.{field, int, list, string, success} 2import gleam/option.{type Option, None} 3 4pub type StrongRef { 5 StrongRef(uri: String, cid: String) 6} 7 8pub fn decode_strong_ref() -> decode.Decoder(StrongRef) { 9 use uri <- field("uri", string) 10 use cid <- field("cid", string) 11 success(StrongRef(uri:, cid:)) 12} 13 14pub type Label { 15 Label(val: String) 16} 17 18pub fn decode_label() -> decode.Decoder(Label) { 19 use val <- field("val", string) 20 success(Label(val:)) 21} 22 23pub type ByteSlice { 24 ByteSlice(byte_start: Int, byte_end: Int) 25} 26 27pub fn decode_byte_slice() -> decode.Decoder(ByteSlice) { 28 use byte_start <- field("byteStart", int) 29 use byte_end <- field("byteEnd", int) 30 success(ByteSlice(byte_start:, byte_end:)) 31} 32 33pub type FacetFeature { 34 Mention(did: String) 35 Link(uri: String) 36 Tag(tag: String) 37} 38 39fn decode_facet_feature() -> decode.Decoder(FacetFeature) { 40 use type_ <- field("$type", string) 41 case type_ { 42 "app.bsky.richtext.facet#mention" -> { 43 use did <- field("did", string) 44 success(Mention(did:)) 45 } 46 "app.bsky.richtext.facet#link" -> { 47 use uri <- field("uri", string) 48 success(Link(uri:)) 49 } 50 "app.bsky.richtext.facet#tag" -> { 51 use tag <- field("tag", string) 52 success(Tag(tag:)) 53 } 54 _ -> decode.failure(Mention(""), expected: "FacetFeature") 55 } 56} 57 58pub type Facet { 59 Facet(index: ByteSlice, features: List(FacetFeature)) 60} 61 62pub fn decode_facet() -> decode.Decoder(Facet) { 63 use index <- field("index", decode_byte_slice()) 64 use features <- field("features", list(of: decode_facet_feature())) 65 success(Facet(index:, features:)) 66} 67 68pub type AspectRatio { 69 AspectRatio(width: Int, height: Int) 70} 71 72pub fn decode_aspect_ratio() -> decode.Decoder(AspectRatio) { 73 use width <- field("width", int) 74 use height <- field("height", int) 75 success(AspectRatio(width:, height:)) 76} 77 78pub type Image { 79 Image( 80 alt: String, 81 thumb: Option(String), 82 fullsize: Option(String), 83 aspect_ratio: Option(AspectRatio), 84 ) 85} 86 87pub fn decode_image() -> decode.Decoder(Image) { 88 use alt <- field("alt", string) 89 success(Image(alt:, thumb: None, fullsize: None, aspect_ratio: None)) 90} 91 92pub type External { 93 External( 94 uri: String, 95 title: String, 96 description: String, 97 thumb: Option(String), 98 ) 99} 100 101pub fn decode_external() -> decode.Decoder(External) { 102 use uri <- field("uri", string) 103 use title <- field("title", string) 104 use description <- field("description", string) 105 success(External(uri:, title:, description:, thumb: None)) 106} 107 108pub type Embed { 109 Images(images: List(Image)) 110 ExternalLink(external: External) 111 Record(StrongRef) 112} 113 114pub fn decode_embed() -> decode.Decoder(Embed) { 115 use type_ <- field("$type", string) 116 case type_ { 117 "app.bsky.embed.images" -> { 118 use images <- field("images", list(of: decode_image())) 119 success(Images(images:)) 120 } 121 "app.bsky.embed.external" -> { 122 use external <- field("external", decode_external()) 123 success(ExternalLink(external:)) 124 } 125 "app.bsky.embed.record" -> { 126 use record <- field("record", decode_strong_ref()) 127 success(Record(record)) 128 } 129 _ -> decode.failure(Images(images: []), expected: "Embed type") 130 } 131} 132 133pub type Post { 134 Post( 135 text: String, 136 facets: List(Facet), 137 embed: Option(Embed), 138 langs: List(String), 139 labels: List(Label), 140 tags: List(String), 141 created_at: String, 142 ) 143} 144 145pub fn decode_post() -> decode.Decoder(Post) { 146 use text <- field("text", string) 147 use created_at <- field("createdAt", string) 148 success(Post( 149 text:, 150 facets: [], 151 embed: None, 152 langs: [], 153 labels: [], 154 tags: [], 155 created_at:, 156 )) 157}