summaryrefslogtreecommitdiff
path: root/shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt
diff options
context:
space:
mode:
Diffstat (limited to 'shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt')
-rw-r--r--shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt74
1 files changed, 74 insertions, 0 deletions
diff --git a/shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt b/shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt
new file mode 100644
index 0000000..680bfcf
--- /dev/null
+++ b/shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt
@@ -0,0 +1,74 @@
+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<Block>) : PostBody
+ data class Foreign(val text: String, val facets: List<Facet> = emptyList()) : PostBody
+}
+
+/** a block of native rich content. will grow (images, embeds, ...). */
+sealed interface Block {
+ data class Paragraph(val text: String, val facets: List<Facet> = 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<String> = emptyList(),
+ val publishedAt: Long,
+ val updatedAt: Long? = null,
+ val parents: List<Post> = emptyList(),
+ /** at-uri of the post this is an addition to, if it is a reblog-with-addition. */
+ val replyTarget: String? = null,
+)