A cheap attempt at a native Bluesky client for Android

Feed: filter out replies to people you don't follow

This commit adds the necessary properties to determine if a skeet is a
reply to someone the user doesn't follow, and then filters the main
feed to hide these skeets.

+33 -8
+3 -1
app/src/main/java/industries/geesawra/monarch/ShowSkeets.kt
··· 39 39 modifier = modifier.fillMaxSize(), 40 40 verticalArrangement = Arrangement.spacedBy(8.dp), 41 41 ) { 42 - viewModel.uiState.skeets.forEach { skeet -> 42 + viewModel.uiState.skeets.filter { 43 + !it.replyToNotFollowing 44 + }.forEach { skeet -> 43 45 item(key = skeet.key()) { 44 46 val root = skeet.root() 45 47 val (parent, parentsParent) = skeet.parent()
+30 -7
app/src/main/java/industries/geesawra/monarch/datalayer/Models.kt
··· 3 3 package industries.geesawra.monarch.datalayer 4 4 5 5 import app.bsky.actor.ProfileView 6 + import app.bsky.actor.ProfileViewBasic 6 7 import app.bsky.embed.RecordViewRecord 7 8 import app.bsky.embed.RecordViewRecordEmbedUnion 8 9 import app.bsky.feed.FeedViewPost ··· 43 44 44 45 val blocked: Boolean = false, 45 46 val notFound: Boolean = false, 47 + val following: Boolean = false, 48 + val follower: Boolean = false, 49 + var replyToNotFollowing: Boolean = false, 46 50 ) { 47 51 companion object { 48 52 fun fromFeedViewPost(post: FeedViewPost): SkeetData { 49 53 val content: Post = (post.post.record.decodeAs()) 50 54 51 - return SkeetData( 55 + val sd = SkeetData( 52 56 likes = post.post.likeCount, 53 57 reposts = post.post.repostCount, 54 58 replies = post.post.replyCount, ··· 64 68 embed = post.post.embed, 65 69 reason = post.reason, 66 70 reply = post.reply, 67 - createdAt = content.createdAt.toStdlibInstant() 71 + createdAt = content.createdAt.toStdlibInstant(), 72 + following = post.post.author.viewer?.following != null, 73 + follower = post.post.author.viewer?.followedBy != null, 68 74 ) 75 + 76 + sd.replyToNotFollowing = { 77 + val (parent, _) = sd.parent() 78 + val root = sd.root() 79 + 80 + val parentFollowing = parent?.following ?: false 81 + val rootFollowing = root?.following ?: false 82 + 83 + val res = parentFollowing && rootFollowing 84 + !res 85 + }() 86 + 87 + return sd 69 88 } 70 89 71 - fun fromPostView(post: PostView): SkeetData { 90 + fun fromPostView(post: PostView, author: ProfileViewBasic): SkeetData { 72 91 val content: Post = (post.record.decodeAs()) 73 92 74 93 return SkeetData( ··· 85 104 authorLabels = post.author.labels, 86 105 content = content.text, 87 106 embed = post.embed, 88 - createdAt = content.createdAt.toStdlibInstant() 107 + createdAt = content.createdAt.toStdlibInstant(), 108 + following = author.viewer?.following != null, 109 + follower = author.viewer?.followedBy != null, 89 110 ) 90 111 } 91 112 ··· 120 141 // }) 121 142 // ) 122 143 // 144 + 123 145 // is PostEmbedUnion.Record -> PostViewEmbedUnion.RecordView( 124 146 // RecordView(post.embed.value.record) 125 147 // ) ··· 222 244 223 245 is ReplyRefParentUnion.PostView -> { 224 246 val content: Post = (rawParent.value.record.decodeAs()) 225 - 226 - fromPostView(rawParent.value) to content.reply?.parent 247 + fromPostView( 248 + rawParent.value, rawParent.value.author 249 + ) to content.reply?.parent 227 250 } 228 251 229 252 else -> null to null ··· 245 268 notFound = rawRoot.value.notFound 246 269 ) 247 270 248 - is ReplyRefRootUnion.PostView -> fromPostView(rawRoot.value) 271 + is ReplyRefRootUnion.PostView -> fromPostView(rawRoot.value, rawRoot.value.author) 249 272 250 273 else -> null 251 274 }