Update comments page UI (#305)

This commit is contained in:
Sasikanth Miriyampalli 2022-02-28 21:14:49 +05:30 committed by GitHub
parent 352769161b
commit 21ced135b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 138 additions and 32 deletions

View file

@ -165,6 +165,7 @@ fun LobstersApp(
postId = postId, postId = postId,
getDetails = viewModel::getPostComments, getDetails = viewModel::getPostComments,
modifier = Modifier.navigationBarsPadding(), modifier = Modifier.navigationBarsPadding(),
postActions = postActions
) )
} }
} }

View file

@ -10,3 +10,5 @@ actual val heartIcon
@Composable get() = painterResource(R.drawable.ic_favorite_24dp) @Composable get() = painterResource(R.drawable.ic_favorite_24dp)
actual val heartBorderIcon actual val heartBorderIcon
@Composable get() = painterResource(R.drawable.ic_favorite_border_24dp) @Composable get() = painterResource(R.drawable.ic_favorite_border_24dp)
actual val webIcon
@Composable get() = painterResource(R.drawable.ic_web_24dp)

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-0.61 0.08,-1.21 0.21,-1.78L8.99,15v1c0,1.1 0.9,2 2,2v1.93C7.06,19.43 4,16.07 4,12zM17.89,17.4c-0.26,-0.81 -1,-1.4 -1.9,-1.4h-1v-3c0,-0.55 -0.45,-1 -1,-1h-6v-2h2c0.55,0 1,-0.45 1,-1L10.99,7h2c1.1,0 2,-0.9 2,-2v-0.41C17.92,5.77 20,8.65 20,12c0,2.08 -0.81,3.98 -2.11,5.4z" />
</vector>

View file

@ -1,26 +1,35 @@
package dev.msfjarvis.claw.common.comments package dev.msfjarvis.claw.common.comments
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.LocalTextStyle import androidx.compose.material.LocalTextStyle
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.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.halilibo.richtext.markdown.Markdown import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.ui.RichTextScope import com.halilibo.richtext.ui.RichTextScope
import com.halilibo.richtext.ui.material.MaterialRichText import com.halilibo.richtext.ui.material.MaterialRichText
import dev.msfjarvis.claw.common.posts.PostDetails import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.posts.PostTitle
import dev.msfjarvis.claw.common.posts.Submitter import dev.msfjarvis.claw.common.posts.Submitter
import dev.msfjarvis.claw.common.posts.toDbModel import dev.msfjarvis.claw.common.posts.TagRow
import dev.msfjarvis.claw.common.ui.Divider import dev.msfjarvis.claw.common.res.webIcon
import dev.msfjarvis.claw.model.Comment import dev.msfjarvis.claw.model.Comment
import dev.msfjarvis.claw.model.LobstersPostDetails import dev.msfjarvis.claw.model.LobstersPostDetails
@ -38,17 +47,65 @@ fun ThemedRichText(
@Composable @Composable
fun CommentsHeader( fun CommentsHeader(
postDetails: LobstersPostDetails, postDetails: LobstersPostDetails,
postActions: PostActions,
) { ) {
val htmlConverter = LocalHTMLConverter.current val htmlConverter = LocalHTMLConverter.current
Surface {
Surface(color = MaterialTheme.colorScheme.background) {
Column( Column(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp).fillMaxWidth(), modifier = Modifier.padding(16.dp).fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
PostDetails( PostTitle(title = postDetails.title)
post = postDetails.toDbModel(), TagRow(tags = postDetails.tags)
Spacer(Modifier.height(4.dp))
if (postDetails.url.isNotBlank()) {
PostLink(
link = postDetails.url,
modifier =
Modifier.clickable { postActions.viewPost(postDetails.url, postDetails.commentsUrl) }
)
Spacer(Modifier.height(4.dp))
}
if (postDetails.description.isNotBlank()) {
ThemedRichText { Markdown(htmlConverter.convertHTMLToMarkdown(postDetails.description)) }
Spacer(Modifier.height(4.dp))
}
Submitter(
text = "Submitted by ${postDetails.submitter.username}",
avatarUrl = "https://lobste.rs/${postDetails.submitter.avatarUrl}",
contentDescription = "User avatar for ${postDetails.submitter.username}",
)
}
}
}
@Composable
fun PostLink(
link: String,
modifier: Modifier = Modifier,
) {
Box(
modifier.background(
color = MaterialTheme.colorScheme.secondary,
shape = RoundedCornerShape(8.dp)
)
) {
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Icon(
painter = webIcon,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSecondary
)
Text(
text = link,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
color = MaterialTheme.colorScheme.onSecondary,
style = MaterialTheme.typography.labelLarge,
) )
ThemedRichText { Markdown(htmlConverter.convertHTMLToMarkdown(postDetails.description)) }
} }
} }
} }
@ -58,17 +115,13 @@ fun CommentEntry(
comment: Comment, comment: Comment,
) { ) {
val htmlConverter = LocalHTMLConverter.current val htmlConverter = LocalHTMLConverter.current
Divider() Box(
Row(modifier = Modifier.wrapContentHeight()) { modifier =
Column( Modifier.fillMaxWidth()
modifier = .background(MaterialTheme.colorScheme.background)
Modifier.padding( .padding(start = (comment.indentLevel * 16).dp, end = 16.dp, top = 16.dp, bottom = 16.dp),
start = (comment.indentLevel * 12).dp, ) {
end = 8.dp, Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
top = 4.dp,
bottom = 4.dp
)
) {
Submitter( Submitter(
text = comment.user.username, text = comment.user.username,
avatarUrl = "https://lobste.rs/${comment.user.avatarUrl}", avatarUrl = "https://lobste.rs/${comment.user.avatarUrl}",

View file

@ -1,36 +1,66 @@
package dev.msfjarvis.claw.common.comments package dev.msfjarvis.claw.common.comments
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Surface
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
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.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.common.NetworkState import dev.msfjarvis.claw.common.NetworkState
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.ui.Divider import dev.msfjarvis.claw.common.ui.Divider
import dev.msfjarvis.claw.model.LobstersPostDetails import dev.msfjarvis.claw.model.LobstersPostDetails
@Composable @Composable
private fun CommentsPageInternal( private fun CommentsPageInternal(
details: LobstersPostDetails, details: LobstersPostDetails,
postActions: PostActions,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
LazyColumn(modifier) { Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
item { CommentsHeader(postDetails = details) } LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
item { CommentsHeader(postDetails = details, postActions = postActions) }
item { Spacer(modifier = Modifier.height(8.dp)) } if (details.commentCount > 0) {
item {
Text(
text = "Comments",
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
)
}
items(details.comments) { item -> CommentEntry(item) } itemsIndexed(details.comments) { index, item ->
if (index != 0) {
item { Divider() } Divider()
}
CommentEntry(item)
}
} else {
item {
Text(
text = "No Comments",
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.fillMaxWidth().padding(16.dp),
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}
}
}
} }
} }
@ -39,6 +69,7 @@ private fun CommentsPageInternal(
fun CommentsPage( fun CommentsPage(
postId: String, postId: String,
getDetails: suspend (String) -> LobstersPostDetails, getDetails: suspend (String) -> LobstersPostDetails,
postActions: PostActions,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val postDetails by val postDetails by
@ -50,7 +81,8 @@ fun CommentsPage(
is NetworkState.Success<*> -> { is NetworkState.Success<*> -> {
CommentsPageInternal( CommentsPageInternal(
(postDetails as NetworkState.Success<LobstersPostDetails>).data, (postDetails as NetworkState.Success<LobstersPostDetails>).data,
modifier, postActions,
modifier.fillMaxSize(),
) )
} }
is NetworkState.Error -> TODO("Handle no network scenario") is NetworkState.Error -> TODO("Handle no network scenario")

View file

@ -140,7 +140,7 @@ fun Submitter(
Row( Row(
modifier = modifier, modifier = modifier,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
NetworkImage( NetworkImage(
url = avatarUrl, url = avatarUrl,

View file

@ -9,3 +9,5 @@ expect val heartIcon: Painter
@Composable get @Composable get
expect val heartBorderIcon: Painter expect val heartBorderIcon: Painter
@Composable get @Composable get
expect val webIcon: Painter
@Composable get

View file

@ -9,3 +9,5 @@ actual val heartIcon
@Composable get() = painterResource("favorite_black_24dp.svg") @Composable get() = painterResource("favorite_black_24dp.svg")
actual val heartBorderIcon actual val heartBorderIcon
@Composable get() = painterResource("favorite_border_black_24dp.svg") @Composable get() = painterResource("favorite_border_black_24dp.svg")
actual val webIcon
@Composable get() = painterResource("web_black_24dp.svg")

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-.61.08-1.21.21-1.78L8.99 15v1c0 1.1.9 2 2 2v1.93C7.06 19.43 4 16.07 4 12zm13.89 5.4c-.26-.81-1-1.4-1.9-1.4h-1v-3c0-.55-.45-1-1-1h-6v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41C17.92 5.77 20 8.65 20 12c0 2.08-.81 3.98-2.11 5.4z" />
</svg>

After

Width:  |  Height:  |  Size: 474 B