diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/comments/CommentEntry.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/comments/CommentEntry.kt deleted file mode 100644 index 2d376dce..00000000 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/comments/CommentEntry.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dev.msfjarvis.claw.android.comments - -import android.text.Html -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.unit.dp -import dev.msfjarvis.claw.model.Comment - -@Composable -fun CommentEntry( - comment: Comment, -) { - Row(modifier = Modifier.padding(start = (10 * (comment.indentLevel.toFloat() - 1)).dp)) { - Text( - text = buildAnnotatedString { @Suppress("DEPRECATION") Html.fromHtml(comment.comment) }, - ) - } -} diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/comments/Comments.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/comments/Comments.kt deleted file mode 100644 index 3fe0ffc4..00000000 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/comments/Comments.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.msfjarvis.claw.android.comments - -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.runtime.Composable -import dev.msfjarvis.claw.model.LobstersPostDetails - -@Composable -private fun CommentsPageInternal( - details: LobstersPostDetails, -) { - LazyColumn { items(details.comments) { CommentEntry(it) } } -} - -@Composable -fun CommentsPage( - postId: String, - getDetails: suspend (String) -> LobstersPostDetails, -) {} diff --git a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt index bcbca4cf..a32cfc4c 100644 --- a/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt +++ b/android/src/main/kotlin/dev/msfjarvis/claw/android/ui/LobstersApp.kt @@ -1,5 +1,6 @@ package dev.msfjarvis.claw.android.ui +import android.text.Html import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.lazy.rememberLazyListState @@ -28,8 +29,8 @@ import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.systemuicontroller.rememberSystemUiController -import dev.msfjarvis.claw.android.comments.CommentsPage import dev.msfjarvis.claw.android.viewmodel.ClawViewModel +import dev.msfjarvis.claw.common.comments.CommentsPage import dev.msfjarvis.claw.common.theme.LobstersTheme import dev.msfjarvis.claw.common.urllauncher.UrlLauncher @@ -78,12 +79,12 @@ fun LobstersApp( topBar = { ClawAppBar(modifier = Modifier.statusBarsPadding()) }, floatingActionButton = { ClawFab( - isFabVisible = isFabVisible, + isFabVisible = isFabVisible && navController.currentDestination?.route == "hottest", listState = listState, modifier = Modifier.navigationBarsPadding(), ) }, - ) { + ) { paddingValues -> NavHost(navController, startDestination = "hottest") { composable("hottest") { HottestPosts( @@ -101,6 +102,8 @@ fun LobstersApp( CommentsPage( postId = requireNotNull(backStackEntry.arguments?.getString("postId")), getDetails = viewModel::getPostComments, + parseHtml = { source -> Html.fromHtml(source).toString().trim() }, + paddingValues = paddingValues, ) } } diff --git a/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/NetworkState.kt b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/NetworkState.kt new file mode 100644 index 00000000..99666597 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/NetworkState.kt @@ -0,0 +1,7 @@ +package dev.msfjarvis.lobsters.ui.comments + +sealed class NetworkState { + class Success(val data: T) : NetworkState() + class Error(val message: String) : NetworkState() + object Loading : NetworkState() +} diff --git a/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt new file mode 100644 index 00000000..fc0afc52 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/comments/CommentEntry.kt @@ -0,0 +1,58 @@ +package dev.msfjarvis.claw.common.comments + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Divider +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import dev.msfjarvis.claw.common.posts.PostDetails +import dev.msfjarvis.claw.common.posts.SubmitterName +import dev.msfjarvis.claw.common.posts.toDbModel +import dev.msfjarvis.claw.model.Comment +import dev.msfjarvis.claw.model.LobstersPostDetails + +@Composable +fun CommentsHeader( + postDetails: LobstersPostDetails, +) { + Surface { + Column( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp).fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + PostDetails( + post = postDetails.toDbModel(), + ) + } + } +} + +@Composable +fun CommentEntry( + comment: Comment, + parseHtml: (String) -> String, +) { + val indentLevel = comment.indentLevel.toInt() - 1 + val startPadding = ((10 * indentLevel) + 16).dp + + Divider(color = Color.Gray.copy(0.4f)) + + Row(modifier = Modifier.padding(start = startPadding, end = 8.dp, top = 4.dp, bottom = 4.dp)) { + val text = parseHtml(comment.comment) + Column { + SubmitterName( + text = "Submitted by ${comment.user.username}", + avatarUrl = "https://lobste.rs/${comment.user.avatarUrl}", + contentDescription = "Submitted by ${comment.user.username}", + ) + Text(text = text, modifier = Modifier.padding(top = 8.dp)) + } + } +} diff --git a/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt new file mode 100644 index 00000000..dd6ea428 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/msfjarvis/claw/common/comments/Comments.kt @@ -0,0 +1,76 @@ +package dev.msfjarvis.claw.common.comments + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.Divider +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import dev.msfjarvis.claw.model.LobstersPostDetails +import dev.msfjarvis.lobsters.ui.comments.NetworkState + +@Composable +private fun CommentsPageInternal( + details: LobstersPostDetails, + parseHtml: (String) -> String, + bottomPadding: Dp, +) { + LazyColumn(Modifier.padding(bottom = bottomPadding)) { + item { CommentsHeader(postDetails = details) } + + item { Spacer(modifier = Modifier.height(8.dp)) } + + items(details.comments) { item -> CommentEntry(item, parseHtml) } + + item { Divider(color = Color.Gray.copy(0.4f)) } + } +} + +@Suppress("UNCHECKED_CAST") +@Composable +fun CommentsPage( + postId: String, + getDetails: suspend (String) -> LobstersPostDetails, + parseHtml: (String) -> String, + paddingValues: PaddingValues, +) { + var postDetails: NetworkState by remember { mutableStateOf(NetworkState.Loading) } + + LaunchedEffect(postId) { postDetails = NetworkState.Success(getDetails(postId)) } + + when (postDetails) { + is NetworkState.Success<*> -> { + CommentsPageInternal( + (postDetails as NetworkState.Success).data, + parseHtml, + paddingValues.calculateBottomPadding(), + ) + } + is NetworkState.Error -> TODO("Handle no network scenario") + NetworkState.Loading -> ProgressBar(paddingValues.calculateBottomPadding()) + } +} + +@Composable +private fun ProgressBar(bottomPadding: Dp) { + Box( + modifier = Modifier.padding(bottom = bottomPadding).fillMaxSize(), + contentAlignment = Alignment.Center + ) { CircularProgressIndicator(color = MaterialTheme.colors.secondary) } +}