diff --git a/app/build.gradle b/app/build.gradle index 361d99595..0739cb297 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt index bde37ac95..0f5972879 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt @@ -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 { + override val values = sequenceOf( + Description("Hello world!

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, diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt index dcabedb48..53a4fa4bf 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentReplies.kt @@ -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>, - disposables: CompositeDisposable + flow: Flow> ) { 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) } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt index 6afbc7e20..e1ed3041e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt @@ -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!! diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt index 1853a134b..2c93999e8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt @@ -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) } }