package dev.serrahima.lluerna.domain /** * the protocol-neutral domain model. * * nothing in here knows about `site.standard.*` (or any other lexicon). the * source adapters map raw records into these types; the ui renders these types. * that boundary is what makes a future bluesky/wafrn adapter additive. * * this is a starter: it will grow as we build the feed. */ /** which protocol a piece of content came from. */ enum class Protocol { STANDARD_SITE } /** a publication (ours) or a foreign actor, depending on [protocol]. */ data class Author( /** did, or an at-uri for a publication. */ val id: String, /** a handle or publication slug, if known. */ val handle: String? = null, val name: String, val avatarUrl: String? = null, /** canonical web url, if the author has one. */ val url: String? = null, val protocol: Protocol = Protocol.STANDARD_SITE, ) /** * a post body is a sum type: either our rich native content, or opaque foreign * content we can only render textually (a future bluesky/wafrn post). */ sealed interface PostBody { data class Native(val blocks: List) : PostBody data class Foreign(val text: String, val facets: List = emptyList()) : PostBody } /** a block of native rich content. will grow (images, embeds, ...). */ sealed interface Block { data class Paragraph(val text: String, val facets: List = emptyList()) : Block data class Header(val level: Int, val text: String) : Block } /** an inline annotation over a byte range of a text. */ sealed interface Facet { val byteStart: Int val byteEnd: Int data class Link(override val byteStart: Int, override val byteEnd: Int, val uri: String) : Facet data class Mention(override val byteStart: Int, override val byteEnd: Int, val uri: String) : Facet data class Bold(override val byteStart: Int, override val byteEnd: Int) : Facet data class Italic(override val byteStart: Int, override val byteEnd: Int) : Facet data class Header(override val byteStart: Int, override val byteEnd: Int, val level: Int) : Facet } /** * a post in the feed. [parents] is the chain toward the root, newest-nearest * first; the tumblr dedup + "tags from every post in the chain" logic both * operate on it. */ data class Post( val uri: String, val cid: String, val author: Author, val body: PostBody, /** plaintext fallback (site.standard.document.textContent). */ val plaintext: String, val tags: List = emptyList(), val publishedAt: Long, val updatedAt: Long? = null, val parents: List = emptyList(), /** at-uri of the post this is an addition to, if it is a reblog-with-addition. */ val replyTarget: String? = null, )