From bda961a04cb8172bd3d5d0c4d7ea61da965e8c72 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 12 May 2024 08:21:13 +0530 Subject: [PATCH] Convert comment replies views to Jetpack Compose --- app/build.gradle | 6 +- .../fragments/list/comments/Comment.kt | 84 ++++++++++++ .../list/comments/CommentRepliesHeader.kt | 121 ++++++++++++++++++ build.gradle | 2 +- 4 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt diff --git a/app/build.gradle b/app/build.gradle index 9ea725ad9..5b5b25350 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,7 +106,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = "1.5.3" + kotlinCompilerExtensionVersion = "1.5.13" } } @@ -267,7 +267,7 @@ dependencies { implementation "com.github.lisawray.groupie:groupie-viewbinding:${groupieVersion}" // Image loading - implementation 'io.coil-kt:coil:2.7.0' + implementation 'io.coil-kt:coil-compose:2.7.0' // Markdown library for Android implementation "io.noties.markwon:core:${markwonVersion}" @@ -289,7 +289,7 @@ dependencies { implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final" // Jetpack Compose - implementation(platform('androidx.compose:compose-bom:2024.02.01')) + implementation(platform('androidx.compose:compose-bom:2024.05.00')) implementation 'androidx.compose.material3:material3' implementation 'androidx.activity:activity-compose' implementation 'androidx.compose.ui:ui-tooling-preview' 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 new file mode 100644 index 000000000..c622aa18d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt @@ -0,0 +1,84 @@ +package org.schabi.newpipe.fragments.list.comments + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.fragment.app.FragmentActivity +import coil.compose.AsyncImage +import org.schabi.newpipe.R +import org.schabi.newpipe.extractor.comments.CommentsInfoItem +import org.schabi.newpipe.extractor.stream.Description +import org.schabi.newpipe.util.NavigationHelper +import org.schabi.newpipe.util.image.ImageStrategy + +@Composable +fun Comment(comment: CommentsInfoItem) { + val context = LocalContext.current + + Row(modifier = Modifier.padding(all = 8.dp)) { + if (ImageStrategy.shouldLoadImages()) { + AsyncImage( + model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars), + contentDescription = null, + placeholder = painterResource(R.drawable.placeholder_person), + error = painterResource(R.drawable.placeholder_person), + modifier = Modifier + .size(42.dp) + .clip(CircleShape) + .clickable { + NavigationHelper.openCommentAuthorIfPresent(context as FragmentActivity, comment) + } + ) + } + + Spacer(modifier = Modifier.width(8.dp)) + + var isExpanded by rememberSaveable { mutableStateOf(false) } + + Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { + Text( + text = comment.uploaderName, + color = MaterialTheme.colors.secondary + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = comment.commentText.content, + // If the comment is expanded, we display all its content + // otherwise we only display the first line + maxLines = if (isExpanded) Int.MAX_VALUE else 1, + style = MaterialTheme.typography.body2 + ) + } + } +} + +@Preview +@Composable +fun CommentPreview() { + val comment = CommentsInfoItem(1, "", "") + comment.commentText = Description("Hello world!", Description.PLAIN_TEXT) + comment.uploaderName = "Test" + + Comment(comment) +} 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 new file mode 100644 index 000000000..243d887cd --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesHeader.kt @@ -0,0 +1,121 @@ +package org.schabi.newpipe.fragments.list.comments + +import android.widget.TextView +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +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.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.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) { + val context = LocalContext.current + + Column(modifier = Modifier.padding(all = 8.dp)) { + Row { + Row( + modifier = Modifier + .padding(all = 8.dp) + .clickable { + NavigationHelper.openCommentAuthorIfPresent( + context as FragmentActivity, + comment + ) + } + ) { + if (ImageStrategy.shouldLoadImages()) { + AsyncImage( + model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars), + contentDescription = null, + placeholder = painterResource(R.drawable.placeholder_person), + error = painterResource(R.drawable.placeholder_person), + modifier = Modifier + .size(42.dp) + .clip(CircleShape) + ) + } + + Spacer(modifier = Modifier.width(8.dp)) + + Column { + Text(text = comment.uploaderName) + + Text( + text = Localization.relativeTimeOrTextual( + context, comment.uploadDate, comment.textualUploadDate + ) + ) + } + } + + if (comment.isHeartedByUploader) { + Image( + painter = painterResource(R.drawable.ic_heart), + contentDescription = stringResource(R.string.detail_heart_img_view_description) + ) + } + + if (comment.isPinned) { + Image( + painter = painterResource(R.drawable.ic_pin), + contentDescription = stringResource(R.string.detail_pinned_comment_view_description) + ) + } + } + + 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 + ) + } + ) + } +} + +@Preview +@Composable +fun CommentRepliesHeaderPreview() { + val disposables = CompositeDisposable() + val comment = CommentsInfoItem(1, "", "") + comment.commentText = Description("Hello world!", Description.PLAIN_TEXT) + comment.uploaderName = "Test" + comment.textualUploadDate = "5 months ago" + + CommentRepliesHeader(comment, disposables) +} diff --git a/build.gradle b/build.gradle index 6d19a6f8a..5a1ae1945 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.9.10' + ext.kotlin_version = '1.9.23' repositories { google() mavenCentral()