A cheap attempt at a native Bluesky client for Android

Refactor: Improve reply filtering logic

Simplify the logic for filtering replies in a thread. The new implementation makes it easier to follow the conditions under which a reply is shown.

- If a user follows the parent and the root of a three-post thread, the reply is shown.
- For threads with four or more posts, the reply is shown if the user follows the parent, root, and grandparent.
- Add `parentsParentRef()` and `grandparentAuthor()` helper functions to traverse the reply chain.

+45 -16
+45 -16
app/src/main/java/industries/geesawra/monarch/datalayer/Models.kt
··· 143 143 val (parent, _) = sd.parent() 144 144 val root = sd.root() 145 145 146 - if (parent?.authorHandle == sd.authorHandle && (root == null || root.authorHandle == sd.authorHandle)) { 147 - return@run false 146 + if (parent == null) { 147 + return@run false // no parent AND no root, this is a single post 148 148 } 149 149 150 - parent?.let { 151 150 152 - val parentFollowing = parent.following 153 - val rootFollowing = root?.following ?: false 151 + if (root == null) { 152 + return@run !parent.following // just parent, this is a single reply 153 + } 154 154 155 - if (root?.authorHandle == sd.authorHandle && parentFollowing) { 156 - return@run false 157 - } 155 + if (!parent.following) { 156 + return@run true // I don't follow whoever sd is replying to, don't care 157 + } 158 158 159 + // Simplify everything: 160 + // - 3 post threads: if we follow parent and root, we show 161 + // - 4+ post threads: if we follow parent, root, and grandfather, we show 159 162 160 - if (!parentFollowing && root == null) { 161 - return@run false 162 - } 163 + // from now on, we know both parent and root are non-null, 164 + // hence we might be in a 3+ post threads. 163 165 164 - val res = parentFollowing || rootFollowing 165 - return@run !res 166 + val parentsParent = sd.parentsParentRef()!! 167 + if (parentsParent.uri == root.uri) { 168 + // parent's root == root, this is a 3 post thread 169 + return@run !root.following 166 170 } 167 171 168 - return@run false 172 + val grandfather = sd.grandparentAuthor() 173 + 174 + // base case: parent, root and grandfather all followed 175 + return@run !(root.following && grandfather?.viewer?.following?.isNotEmpty() ?: false) 169 176 } 177 + 178 + 170 179 } 171 180 } 172 181 ··· 547 556 ) 548 557 } 549 558 559 + fun parentsParentRef(): StrongRef? { 560 + val rawParent = this.reply?.parent 561 + return when (rawParent) { 562 + is ReplyRefParentUnion.PostView -> { 563 + val content: Post = (rawParent.value.record.decodeAs()) 564 + when (content.reply) { 565 + is PostReplyRef -> content.reply!!.parent 566 + else -> null 567 + } 568 + } 569 + 570 + else -> null 571 + } 572 + } 573 + 550 574 fun parent(): Pair<SkeetData?, StrongRef?> { 551 575 val rawParent = this.reply?.parent 552 576 return when (rawParent) { ··· 564 588 565 589 is ReplyRefParentUnion.PostView -> { 566 590 val content: Post = (rawParent.value.record.decodeAs()) 567 - fromPostView( 568 - rawParent.value, rawParent.value.author 591 + fromPost( 592 + (rawParent.value.cid to rawParent.value.uri), 593 + content, rawParent.value.author 569 594 ) to content.reply?.parent 570 595 } 571 596 572 597 else -> null to null 573 598 } 599 + } 600 + 601 + fun grandparentAuthor(): ProfileViewBasic? { 602 + return this.reply?.grandparentAuthor 574 603 } 575 604 576 605 fun root(): SkeetData? {