mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-01-09 17:00:32 +00:00
Use AnnotatedString to handle HTML parsing
This commit is contained in:
parent
e75eb2d544
commit
c29fa70080
@ -293,6 +293,7 @@ dependencies {
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation 'androidx.activity:activity-compose'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'androidx.compose.ui:ui-text:1.7.0-beta03' // Needed for parsing HTML to AnnotatedString
|
||||
|
||||
// Paging
|
||||
implementation 'androidx.paging:paging-rxjava3:3.3.0'
|
||||
|
@ -18,6 +18,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -25,8 +26,13 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ParagraphStyle
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import coil.compose.AsyncImage
|
||||
@ -38,6 +44,18 @@ import org.schabi.newpipe.util.Localization
|
||||
import org.schabi.newpipe.util.NavigationHelper
|
||||
import org.schabi.newpipe.util.image.ImageStrategy
|
||||
|
||||
@Composable
|
||||
fun rememberParsedText(commentText: Description): AnnotatedString {
|
||||
// TODO: Handle links and hashtags, Markdown.
|
||||
return remember(commentText) {
|
||||
if (commentText.type == Description.HTML) {
|
||||
AnnotatedString.fromHtml(commentText.content)
|
||||
} else {
|
||||
AnnotatedString(commentText.content, ParagraphStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Comment(comment: CommentsInfoItem) {
|
||||
val context = LocalContext.current
|
||||
@ -79,23 +97,22 @@ fun Comment(comment: CommentsInfoItem) {
|
||||
)
|
||||
}
|
||||
|
||||
val date = Localization.relativeTimeOrTextual(
|
||||
context, comment.uploadDate, comment.textualUploadDate
|
||||
)
|
||||
Text(
|
||||
text = Localization.concatenateStrings(comment.uploaderName, date),
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
val nameAndDate = remember(comment) {
|
||||
val date = Localization.relativeTimeOrTextual(
|
||||
context, comment.uploadDate, comment.textualUploadDate
|
||||
)
|
||||
Localization.concatenateStrings(comment.uploaderName, date)
|
||||
}
|
||||
Text(text = nameAndDate, color = MaterialTheme.colorScheme.secondary)
|
||||
}
|
||||
|
||||
// TODO: Handle HTML and Markdown formats.
|
||||
Text(
|
||||
text = comment.commentText.content,
|
||||
text = rememberParsedText(comment.commentText),
|
||||
// If the comment is expanded, we display all its content
|
||||
// otherwise we only display the first two lines
|
||||
maxLines = if (isExpanded) Int.MAX_VALUE else 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
@ -138,12 +155,21 @@ fun CommentsInfoItem(
|
||||
this.isPinned = isPinned
|
||||
}
|
||||
|
||||
class DescriptionPreviewProvider : PreviewParameterProvider<Description> {
|
||||
override val values = sequenceOf(
|
||||
Description("Hello world!<br><br>This line should be hidden by default.", Description.HTML),
|
||||
Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun CommentPreview() {
|
||||
private fun CommentPreview(
|
||||
@PreviewParameter(DescriptionPreviewProvider::class) description: Description
|
||||
) {
|
||||
val comment = CommentsInfoItem(
|
||||
commentText = Description("Hello world!\n\nThis line should be hidden by default.", Description.PLAIN_TEXT),
|
||||
commentText = description,
|
||||
uploaderName = "Test",
|
||||
likeCount = 100,
|
||||
isPinned = true,
|
||||
|
@ -8,7 +8,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
|
||||
@ -18,14 +17,13 @@ import org.schabi.newpipe.ui.theme.AppTheme
|
||||
@Composable
|
||||
fun CommentReplies(
|
||||
comment: CommentsInfoItem,
|
||||
flow: Flow<PagingData<CommentsInfoItem>>,
|
||||
disposables: CompositeDisposable
|
||||
flow: Flow<PagingData<CommentsInfoItem>>
|
||||
) {
|
||||
val replies = flow.collectAsLazyPagingItems()
|
||||
|
||||
LazyColumn {
|
||||
item {
|
||||
CommentRepliesHeader(comment = comment, disposables = disposables)
|
||||
CommentRepliesHeader(comment = comment)
|
||||
HorizontalDivider(thickness = 1.dp)
|
||||
}
|
||||
|
||||
@ -58,6 +56,6 @@ private fun CommentRepliesPreview() {
|
||||
val flow = flowOf(PagingData.from(listOf(reply1, reply2)))
|
||||
|
||||
AppTheme {
|
||||
CommentReplies(comment = comment, flow = flow, disposables = CompositeDisposable())
|
||||
CommentReplies(comment = comment, flow = flow)
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,11 @@ import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
|
||||
import org.schabi.newpipe.ktx.serializable
|
||||
import org.schabi.newpipe.ui.theme.AppTheme
|
||||
|
||||
class CommentRepliesFragment : Fragment() {
|
||||
private val disposables = CompositeDisposable()
|
||||
lateinit var comment: CommentsInfoItem
|
||||
|
||||
override fun onCreateView(
|
||||
@ -33,17 +31,12 @@ class CommentRepliesFragment : Fragment() {
|
||||
}
|
||||
|
||||
AppTheme {
|
||||
CommentReplies(comment = comment, flow = flow, disposables = disposables)
|
||||
CommentReplies(comment = comment, flow = flow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG = CommentRepliesFragment::class.simpleName!!
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.schabi.newpipe.fragments.list.comments
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.widget.TextView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -25,24 +24,18 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.method.LinkMovementMethodCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import coil.compose.AsyncImage
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
|
||||
import org.schabi.newpipe.extractor.stream.Description
|
||||
import org.schabi.newpipe.ui.theme.AppTheme
|
||||
import org.schabi.newpipe.util.Localization
|
||||
import org.schabi.newpipe.util.NavigationHelper
|
||||
import org.schabi.newpipe.util.ServiceHelper
|
||||
import org.schabi.newpipe.util.image.ImageStrategy
|
||||
import org.schabi.newpipe.util.text.TextLinkifier
|
||||
|
||||
@Composable
|
||||
fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDisposable) {
|
||||
fun CommentRepliesHeader(comment: CommentsInfoItem) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
@ -117,20 +110,9 @@ fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDispos
|
||||
}
|
||||
}
|
||||
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
TextView(context).apply {
|
||||
movementMethod = LinkMovementMethodCompat.getInstance()
|
||||
}
|
||||
},
|
||||
update = { view ->
|
||||
// setup comment content
|
||||
TextLinkifier.fromDescription(
|
||||
view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY,
|
||||
ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables,
|
||||
null
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = rememberParsedText(comment.commentText),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -149,6 +131,6 @@ fun CommentRepliesHeaderPreview() {
|
||||
)
|
||||
|
||||
AppTheme {
|
||||
CommentRepliesHeader(comment, CompositeDisposable())
|
||||
CommentRepliesHeader(comment)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user