summaryrefslogtreecommitdiff
path: root/shared/src/commonMain/kotlin/dev/serrahima/lluerna/domain/Model.kt
blob: 680bfcf585a98223ac490681223291b30b315922 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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,
)