2022-10-26 01:55:50 +05:30
|
|
|
/*
|
2024-01-18 01:53:00 +05:30
|
|
|
* Copyright © 2021-2024 Harsh Shandilya.
|
2022-10-26 01:55:50 +05:30
|
|
|
* 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.
|
|
|
|
*/
|
2021-10-04 17:34:04 +05:30
|
|
|
package dev.msfjarvis.claw.common.comments
|
|
|
|
|
2022-09-28 21:08:24 +05:30
|
|
|
import android.text.format.DateUtils
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.foundation.background
|
|
|
|
import androidx.compose.foundation.clickable
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.foundation.layout.Box
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
import androidx.compose.foundation.layout.Row
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.foundation.layout.Spacer
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.foundation.layout.height
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.foundation.layout.padding
|
2022-08-19 23:40:18 +05:30
|
|
|
import androidx.compose.foundation.layout.size
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
2023-05-18 01:52:35 +05:30
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
|
import androidx.compose.material.icons.filled.Public
|
2021-12-31 01:14:39 +05:30
|
|
|
import androidx.compose.material3.MaterialTheme
|
2021-12-30 10:49:21 +05:30
|
|
|
import androidx.compose.material3.Surface
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.material3.Text
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.runtime.Composable
|
2022-03-08 16:42:11 +05:30
|
|
|
import androidx.compose.runtime.getValue
|
2022-10-16 12:21:19 +05:30
|
|
|
import androidx.compose.runtime.produceState
|
2022-03-08 16:42:11 +05:30
|
|
|
import androidx.compose.runtime.remember
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.ui.Modifier
|
2022-05-10 14:41:07 +05:30
|
|
|
import androidx.compose.ui.platform.LocalUriHandler
|
2022-09-28 20:56:45 +05:30
|
|
|
import androidx.compose.ui.text.AnnotatedString
|
2023-01-12 00:59:31 +05:30
|
|
|
import androidx.compose.ui.text.SpanStyle
|
2022-09-28 21:08:24 +05:30
|
|
|
import androidx.compose.ui.text.buildAnnotatedString
|
2023-01-12 00:59:31 +05:30
|
|
|
import androidx.compose.ui.text.font.FontWeight
|
2022-02-28 21:14:49 +05:30
|
|
|
import androidx.compose.ui.text.style.TextOverflow
|
2023-01-12 00:59:31 +05:30
|
|
|
import androidx.compose.ui.text.withStyle
|
2021-10-04 17:34:04 +05:30
|
|
|
import androidx.compose.ui.unit.dp
|
2023-09-28 14:09:09 +05:30
|
|
|
import com.github.michaelbull.result.coroutines.runSuspendCatching
|
|
|
|
import com.github.michaelbull.result.onSuccess
|
2022-02-28 21:14:49 +05:30
|
|
|
import dev.msfjarvis.claw.common.posts.PostActions
|
|
|
|
import dev.msfjarvis.claw.common.posts.PostTitle
|
2022-02-20 15:23:26 +05:30
|
|
|
import dev.msfjarvis.claw.common.posts.Submitter
|
2022-02-28 21:14:49 +05:30
|
|
|
import dev.msfjarvis.claw.common.posts.TagRow
|
2022-08-19 23:40:18 +05:30
|
|
|
import dev.msfjarvis.claw.common.ui.NetworkImage
|
2023-06-28 10:58:39 +05:30
|
|
|
import dev.msfjarvis.claw.common.ui.ThemedRichText
|
2022-08-19 23:40:18 +05:30
|
|
|
import dev.msfjarvis.claw.model.LinkMetadata
|
2024-01-27 16:52:47 +05:30
|
|
|
import dev.msfjarvis.claw.model.UIPost
|
2022-09-28 21:08:24 +05:30
|
|
|
import java.time.Instant
|
|
|
|
import java.time.temporal.TemporalAccessor
|
2023-03-08 02:23:49 +05:30
|
|
|
import kotlinx.collections.immutable.toImmutableList
|
2021-10-04 17:34:04 +05:30
|
|
|
|
|
|
|
@Composable
|
2022-12-12 12:47:23 +05:30
|
|
|
internal fun CommentsHeader(
|
2024-01-27 16:52:47 +05:30
|
|
|
post: UIPost,
|
2022-02-28 21:14:49 +05:30
|
|
|
postActions: PostActions,
|
2023-06-28 10:58:39 +05:30
|
|
|
htmlConverter: HTMLConverter,
|
2022-10-03 11:56:59 +05:30
|
|
|
modifier: Modifier = Modifier,
|
2021-10-04 17:34:04 +05:30
|
|
|
) {
|
2022-05-10 14:41:07 +05:30
|
|
|
val uriHandler = LocalUriHandler.current
|
2022-10-16 12:21:19 +05:30
|
|
|
val linkMetadata by
|
2024-01-27 16:52:47 +05:30
|
|
|
produceState(initialValue = LinkMetadata(post.url, null)) {
|
|
|
|
runSuspendCatching { postActions.getLinkMetadata(post.url) }
|
2022-10-18 12:08:49 +05:30
|
|
|
.onSuccess { metadata -> value = metadata }
|
2022-10-16 12:21:19 +05:30
|
|
|
}
|
2022-02-28 21:14:49 +05:30
|
|
|
|
2022-10-03 11:56:59 +05:30
|
|
|
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
|
2021-10-04 17:34:04 +05:30
|
|
|
Column(
|
2022-02-28 21:14:49 +05:30
|
|
|
modifier = Modifier.padding(16.dp).fillMaxWidth(),
|
|
|
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
2021-10-04 17:34:04 +05:30
|
|
|
) {
|
2024-01-27 16:52:47 +05:30
|
|
|
PostTitle(title = post.title, isRead = false)
|
|
|
|
TagRow(tags = post.tags.toImmutableList())
|
2022-02-28 21:14:49 +05:30
|
|
|
Spacer(Modifier.height(4.dp))
|
|
|
|
|
2022-10-16 12:21:19 +05:30
|
|
|
if (linkMetadata.url.isNotBlank()) {
|
2022-02-28 21:14:49 +05:30
|
|
|
PostLink(
|
2022-10-16 12:21:19 +05:30
|
|
|
linkMetadata = linkMetadata,
|
2022-02-28 21:14:49 +05:30
|
|
|
modifier =
|
2023-08-16 00:02:24 +05:30
|
|
|
Modifier.clickable {
|
2024-01-27 16:52:47 +05:30
|
|
|
postActions.viewPost(post.shortId, linkMetadata.url, post.commentsUrl)
|
2023-08-16 00:02:24 +05:30
|
|
|
},
|
2022-02-28 21:14:49 +05:30
|
|
|
)
|
|
|
|
Spacer(Modifier.height(4.dp))
|
|
|
|
}
|
|
|
|
|
2024-01-27 16:52:47 +05:30
|
|
|
if (post.description.isNotBlank()) {
|
|
|
|
ThemedRichText(htmlConverter.convertHTMLToMarkdown(post.description))
|
2022-02-28 21:14:49 +05:30
|
|
|
Spacer(Modifier.height(4.dp))
|
|
|
|
}
|
|
|
|
Submitter(
|
2024-03-16 09:48:08 +05:30
|
|
|
text = AnnotatedString("Submitted by ${post.submitter}"),
|
|
|
|
avatarUrl = "https://lobste.rs/avatars/${post.submitter}-100.png",
|
|
|
|
contentDescription = "User avatar for ${post.submitter}",
|
2022-05-10 14:41:07 +05:30
|
|
|
modifier =
|
2024-03-16 09:48:08 +05:30
|
|
|
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${post.submitter}") },
|
2022-02-28 21:14:49 +05:30
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
2024-01-18 01:53:00 +05:30
|
|
|
private fun PostLink(linkMetadata: LinkMetadata, modifier: Modifier = Modifier) {
|
2022-02-28 21:14:49 +05:30
|
|
|
Box(
|
|
|
|
modifier.background(
|
|
|
|
color = MaterialTheme.colorScheme.secondary,
|
2022-02-28 21:32:20 +05:30
|
|
|
shape = RoundedCornerShape(8.dp),
|
2022-02-28 21:14:49 +05:30
|
|
|
)
|
|
|
|
) {
|
2022-12-09 18:18:18 +05:30
|
|
|
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
2022-12-09 18:18:02 +05:30
|
|
|
NetworkImage(
|
|
|
|
url = linkMetadata.faviconUrl,
|
2023-06-04 21:47:34 +05:30
|
|
|
placeholder = Icons.Filled.Public,
|
2022-12-09 18:18:02 +05:30
|
|
|
contentDescription = "",
|
|
|
|
modifier = Modifier.size(24.dp),
|
|
|
|
)
|
2022-02-28 21:14:49 +05:30
|
|
|
Text(
|
2022-08-19 23:40:18 +05:30
|
|
|
text = linkMetadata.url,
|
2022-02-28 21:14:49 +05:30
|
|
|
overflow = TextOverflow.Ellipsis,
|
|
|
|
maxLines = 1,
|
|
|
|
color = MaterialTheme.colorScheme.onSecondary,
|
|
|
|
style = MaterialTheme.typography.labelLarge,
|
2021-10-04 17:34:04 +05:30
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-05 11:50:22 +05:30
|
|
|
private val CommentEntryPadding = 16f.dp
|
|
|
|
|
2021-10-04 17:34:04 +05:30
|
|
|
@Composable
|
2022-12-12 12:47:23 +05:30
|
|
|
internal fun CommentEntry(
|
2022-12-10 19:28:38 +05:30
|
|
|
commentNode: CommentNode,
|
2023-06-28 10:58:39 +05:30
|
|
|
htmlConverter: HTMLConverter,
|
2022-12-10 19:28:38 +05:30
|
|
|
toggleExpanded: (CommentNode) -> Unit,
|
2022-10-03 11:56:59 +05:30
|
|
|
modifier: Modifier = Modifier,
|
2021-10-04 17:34:04 +05:30
|
|
|
) {
|
2022-05-10 14:41:07 +05:30
|
|
|
val uriHandler = LocalUriHandler.current
|
2022-12-10 19:28:38 +05:30
|
|
|
val comment = commentNode.comment
|
2022-02-28 21:14:49 +05:30
|
|
|
Box(
|
|
|
|
modifier =
|
2022-10-03 11:56:59 +05:30
|
|
|
modifier
|
|
|
|
.fillMaxWidth()
|
2022-12-10 19:28:38 +05:30
|
|
|
.clickable { toggleExpanded(commentNode) }
|
2022-02-28 21:14:49 +05:30
|
|
|
.background(MaterialTheme.colorScheme.background)
|
2022-10-05 11:50:22 +05:30
|
|
|
.padding(
|
2023-08-30 14:34:23 +05:30
|
|
|
start = CommentEntryPadding * commentNode.indentLevel,
|
2022-10-05 11:50:22 +05:30
|
|
|
end = CommentEntryPadding,
|
|
|
|
top = CommentEntryPadding,
|
|
|
|
bottom = CommentEntryPadding,
|
2024-01-18 01:53:00 +05:30
|
|
|
)
|
2022-02-28 21:14:49 +05:30
|
|
|
) {
|
|
|
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
2022-02-20 15:23:26 +05:30
|
|
|
Submitter(
|
2022-09-28 21:08:24 +05:30
|
|
|
text =
|
|
|
|
buildCommenterString(
|
2024-03-16 09:48:08 +05:30
|
|
|
commenterName = comment.user,
|
2022-09-28 21:08:24 +05:30
|
|
|
score = comment.score,
|
2023-01-12 00:59:31 +05:30
|
|
|
isUnread = commentNode.isUnread,
|
2022-09-28 21:08:24 +05:30
|
|
|
createdAt = comment.createdAt,
|
|
|
|
updatedAt = comment.updatedAt,
|
|
|
|
),
|
2024-03-16 09:48:08 +05:30
|
|
|
avatarUrl = "https://lobste.rs/avatars/${comment.user}-100.png",
|
|
|
|
contentDescription = "User avatar for ${comment.user}",
|
|
|
|
modifier = Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user}") },
|
2021-10-04 17:34:04 +05:30
|
|
|
)
|
2023-06-07 02:42:15 +05:30
|
|
|
if (commentNode.isExpanded) {
|
2023-07-27 01:28:21 +05:30
|
|
|
ThemedRichText(
|
|
|
|
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
|
2024-01-18 01:53:00 +05:30
|
|
|
modifier = Modifier.padding(top = 8.dp),
|
2023-07-27 01:28:21 +05:30
|
|
|
)
|
2021-10-26 17:53:15 +05:30
|
|
|
}
|
2021-10-04 17:34:04 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-28 21:08:24 +05:30
|
|
|
|
|
|
|
@Composable
|
|
|
|
fun buildCommenterString(
|
|
|
|
commenterName: String,
|
|
|
|
score: Int,
|
2023-01-12 00:59:31 +05:30
|
|
|
isUnread: Boolean,
|
2022-09-28 21:08:24 +05:30
|
|
|
createdAt: TemporalAccessor,
|
|
|
|
updatedAt: TemporalAccessor,
|
|
|
|
): AnnotatedString {
|
2023-07-19 15:32:20 +05:30
|
|
|
val now = System.currentTimeMillis()
|
2022-09-28 21:08:24 +05:30
|
|
|
val createdRelative =
|
|
|
|
remember(createdAt) {
|
|
|
|
DateUtils.getRelativeTimeSpanString(
|
|
|
|
Instant.from(createdAt).toEpochMilli(),
|
|
|
|
now,
|
|
|
|
DateUtils.MINUTE_IN_MILLIS,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
val updatedRelative =
|
|
|
|
remember(updatedAt) {
|
|
|
|
DateUtils.getRelativeTimeSpanString(
|
|
|
|
Instant.from(updatedAt).toEpochMilli(),
|
|
|
|
now,
|
|
|
|
DateUtils.MINUTE_IN_MILLIS,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return buildAnnotatedString {
|
|
|
|
append(commenterName)
|
|
|
|
append(' ')
|
|
|
|
append('•')
|
|
|
|
append(' ')
|
|
|
|
append("$score points")
|
|
|
|
append(' ')
|
|
|
|
append('•')
|
|
|
|
append(' ')
|
2022-09-28 23:50:03 +05:30
|
|
|
append(createdRelative.toString())
|
2022-09-28 21:08:24 +05:30
|
|
|
if (updatedRelative != createdRelative) {
|
|
|
|
append(' ')
|
|
|
|
append('(')
|
|
|
|
append("Updated")
|
|
|
|
append(' ')
|
|
|
|
append(updatedRelative.toString())
|
|
|
|
append(')')
|
|
|
|
}
|
2023-01-12 00:59:31 +05:30
|
|
|
if (isUnread) {
|
|
|
|
append(' ')
|
|
|
|
withStyle(
|
2024-01-18 01:53:00 +05:30
|
|
|
style = SpanStyle(fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.error)
|
2023-01-12 00:59:31 +05:30
|
|
|
) {
|
|
|
|
append("(unread)")
|
|
|
|
}
|
|
|
|
}
|
2022-09-28 21:08:24 +05:30
|
|
|
}
|
|
|
|
}
|