refactor(comments): convert internal representation to a tree

This commit is contained in:
Anunay Maheshwari 2022-12-10 19:28:38 +05:30 committed by Harsh Shandilya
parent 0d668ac3aa
commit 18d82a5eb3
No known key found for this signature in database
3 changed files with 86 additions and 14 deletions

View file

@ -26,10 +26,8 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
@ -43,7 +41,6 @@ import dev.msfjarvis.claw.common.posts.TagRow
import dev.msfjarvis.claw.common.res.ClawIcons import dev.msfjarvis.claw.common.res.ClawIcons
import dev.msfjarvis.claw.common.ui.NetworkImage import dev.msfjarvis.claw.common.ui.NetworkImage
import dev.msfjarvis.claw.common.ui.ThemedRichText import dev.msfjarvis.claw.common.ui.ThemedRichText
import dev.msfjarvis.claw.model.Comment
import dev.msfjarvis.claw.model.LinkMetadata import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.LobstersPostDetails import dev.msfjarvis.claw.model.LobstersPostDetails
import java.time.Instant import java.time.Instant
@ -134,17 +131,18 @@ private val CommentEntryPadding = 16f.dp
@OptIn(ExperimentalAnimationApi::class) @OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun CommentEntry( fun CommentEntry(
comment: Comment, commentNode: CommentNode,
htmlConverter: HTMLConverter, htmlConverter: HTMLConverter,
toggleExpanded: (CommentNode) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
var expanded by remember(comment) { mutableStateOf(true) }
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val comment = commentNode.comment
Box( Box(
modifier = modifier =
modifier modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { expanded = !expanded } .clickable { toggleExpanded(commentNode) }
.background(MaterialTheme.colorScheme.background) .background(MaterialTheme.colorScheme.background)
.padding( .padding(
start = CommentEntryPadding * comment.indentLevel, start = CommentEntryPadding * comment.indentLevel,
@ -167,7 +165,7 @@ fun CommentEntry(
modifier = modifier =
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") }, Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") },
) )
AnimatedContent(targetState = expanded) { expandedState -> AnimatedContent(targetState = commentNode.isExpanded) { expandedState ->
if (expandedState) { if (expandedState) {
ThemedRichText( ThemedRichText(
text = htmlConverter.convertHTMLToMarkdown(comment.comment), text = htmlConverter.convertHTMLToMarkdown(comment.comment),

View file

@ -0,0 +1,66 @@
/*
* Copyright © 2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.common.comments
import androidx.compose.material3.Divider
import androidx.compose.runtime.Composable
import dev.msfjarvis.claw.model.Comment
data class CommentNode(
val comment: Comment,
var parent: CommentNode? = null,
val children: MutableList<CommentNode> = mutableListOf(),
var isExpanded: Boolean = true
) {
fun addChild(child: CommentNode) {
if (comment.indentLevel + 1 == child.comment.indentLevel) {
children.add(child)
child.parent = this
} else {
children.last().addChild(child)
}
}
}
fun createListNode(comments: List<Comment>): MutableList<CommentNode> {
val commentNodes = mutableListOf<CommentNode>()
for (i in comments.indices) {
if (comments[i].indentLevel == 1) {
commentNodes.add(CommentNode(comment = comments[i]))
} else {
commentNodes.last().addChild(CommentNode(comment = comments[i]))
}
}
return commentNodes
}
@Composable
fun DisplayListNode(
comments: List<CommentNode>,
htmlConverter: HTMLConverter,
updateComments: (CommentNode) -> Unit,
) {
comments.forEach {
CommentEntry(commentNode = it, htmlConverter = htmlConverter, updateComments)
Divider()
if (it.children.isNotEmpty()) {
DisplayListNode(comments = it.children, htmlConverter = htmlConverter, updateComments)
}
}
}
fun toggleAllExpanded(commentNode: CommentNode): CommentNode {
commentNode.isExpanded = !commentNode.isExpanded
if (commentNode.children.isNotEmpty()) {
commentNode.children.forEach { toggleAllExpanded(it) }
}
return commentNode
}

View file

@ -11,14 +11,13 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -39,6 +38,8 @@ private fun CommentsPageInternal(
htmlConverter: HTMLConverter, htmlConverter: HTMLConverter,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val commentNodes = createListNode(details.comments).toMutableStateList()
Surface(color = MaterialTheme.colorScheme.surfaceVariant) { Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) { LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
item { item {
@ -49,7 +50,7 @@ private fun CommentsPageInternal(
) )
} }
if (details.comments.isNotEmpty()) { if (commentNodes.isNotEmpty()) {
item { item {
Text( Text(
text = "Comments", text = "Comments",
@ -58,11 +59,18 @@ private fun CommentsPageInternal(
) )
} }
itemsIndexed(details.comments) { index, item -> item {
if (index != 0) { DisplayListNode(comments = commentNodes, htmlConverter = htmlConverter) { node ->
Divider() val newNode = toggleAllExpanded(node)
val index =
commentNodes.indexOf(commentNodes.find { it.comment.url == node.comment.url })
if (index == -1) {
error("Failed to find node for comment: ${node.comment}")
} else {
commentNodes.removeAt(index)
commentNodes.add(index, newNode)
}
} }
CommentEntry(comment = item, htmlConverter = htmlConverter)
} }
} else { } else {
item { item {