From 18d82a5eb3f3b9ce450659d8e049026c3997a452 Mon Sep 17 00:00:00 2001 From: Anunay Maheshwari Date: Sat, 10 Dec 2022 19:28:38 +0530 Subject: [PATCH] refactor(comments): convert internal representation to a tree --- .../claw/common/comments/CommentEntry.kt | 12 ++-- .../claw/common/comments/CommentNode.kt | 66 +++++++++++++++++++ .../claw/common/comments/Comments.kt | 22 +++++-- 3 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt index feb05d42..a065b633 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt @@ -26,10 +26,8 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler 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.ui.NetworkImage import dev.msfjarvis.claw.common.ui.ThemedRichText -import dev.msfjarvis.claw.model.Comment import dev.msfjarvis.claw.model.LinkMetadata import dev.msfjarvis.claw.model.LobstersPostDetails import java.time.Instant @@ -134,17 +131,18 @@ private val CommentEntryPadding = 16f.dp @OptIn(ExperimentalAnimationApi::class) @Composable fun CommentEntry( - comment: Comment, + commentNode: CommentNode, htmlConverter: HTMLConverter, + toggleExpanded: (CommentNode) -> Unit, modifier: Modifier = Modifier, ) { - var expanded by remember(comment) { mutableStateOf(true) } val uriHandler = LocalUriHandler.current + val comment = commentNode.comment Box( modifier = modifier .fillMaxWidth() - .clickable { expanded = !expanded } + .clickable { toggleExpanded(commentNode) } .background(MaterialTheme.colorScheme.background) .padding( start = CommentEntryPadding * comment.indentLevel, @@ -167,7 +165,7 @@ fun CommentEntry( modifier = Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") }, ) - AnimatedContent(targetState = expanded) { expandedState -> + AnimatedContent(targetState = commentNode.isExpanded) { expandedState -> if (expandedState) { ThemedRichText( text = htmlConverter.convertHTMLToMarkdown(comment.comment), diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt new file mode 100644 index 00000000..82cbabb7 --- /dev/null +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/CommentNode.kt @@ -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 = 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): MutableList { + val commentNodes = mutableListOf() + + 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, + 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 +} diff --git a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt index 34f4a992..8a7a588c 100644 --- a/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt +++ b/common/src/main/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt @@ -11,14 +11,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding 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.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -39,6 +38,8 @@ private fun CommentsPageInternal( htmlConverter: HTMLConverter, modifier: Modifier = Modifier, ) { + val commentNodes = createListNode(details.comments).toMutableStateList() + Surface(color = MaterialTheme.colorScheme.surfaceVariant) { LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) { item { @@ -49,7 +50,7 @@ private fun CommentsPageInternal( ) } - if (details.comments.isNotEmpty()) { + if (commentNodes.isNotEmpty()) { item { Text( text = "Comments", @@ -58,11 +59,18 @@ private fun CommentsPageInternal( ) } - itemsIndexed(details.comments) { index, item -> - if (index != 0) { - Divider() + item { + DisplayListNode(comments = commentNodes, htmlConverter = htmlConverter) { node -> + 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 { item {