mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	Added missing comment features, fixed theming
This commit is contained in:
		| @@ -871,7 +871,7 @@ public class MainActivity extends AppCompatActivity { | |||||||
|         @Nullable final CommentRepliesFragment repliesFragment = |         @Nullable final CommentRepliesFragment repliesFragment = | ||||||
|                 (CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG); |                 (CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG); | ||||||
|         @Nullable final CommentsInfoItem rootComment = |         @Nullable final CommentsInfoItem rootComment = | ||||||
|                 repliesFragment == null ? null : repliesFragment.commentsInfoItem; |                 repliesFragment == null ? null : repliesFragment.getCommentsInfoItem(); | ||||||
|  |  | ||||||
|         // sometimes this function pops the backstack, other times it's handled by the system |         // sometimes this function pops the backstack, other times it's handled by the system | ||||||
|         if (popBackStack) { |         if (popBackStack) { | ||||||
|   | |||||||
| @@ -1,16 +1,19 @@ | |||||||
| package org.schabi.newpipe.fragments.list.comments | package org.schabi.newpipe.fragments.list.comments | ||||||
|  |  | ||||||
|  | import android.content.res.Configuration | ||||||
|  | import androidx.compose.foundation.Image | ||||||
| import androidx.compose.foundation.clickable | import androidx.compose.foundation.clickable | ||||||
|  | import androidx.compose.foundation.layout.Arrangement | ||||||
| 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.Spacer | ||||||
| import androidx.compose.foundation.layout.height |  | ||||||
| import androidx.compose.foundation.layout.padding | import androidx.compose.foundation.layout.padding | ||||||
| import androidx.compose.foundation.layout.size | import androidx.compose.foundation.layout.size | ||||||
| import androidx.compose.foundation.layout.width | import androidx.compose.foundation.layout.width | ||||||
| import androidx.compose.foundation.shape.CircleShape | import androidx.compose.foundation.shape.CircleShape | ||||||
| import androidx.compose.material.MaterialTheme | import androidx.compose.material3.MaterialTheme | ||||||
| import androidx.compose.material.Text | import androidx.compose.material3.Surface | ||||||
|  | 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.mutableStateOf | import androidx.compose.runtime.mutableStateOf | ||||||
| @@ -20,6 +23,7 @@ import androidx.compose.ui.Modifier | |||||||
| import androidx.compose.ui.draw.clip | import androidx.compose.ui.draw.clip | ||||||
| import androidx.compose.ui.platform.LocalContext | import androidx.compose.ui.platform.LocalContext | ||||||
| import androidx.compose.ui.res.painterResource | import androidx.compose.ui.res.painterResource | ||||||
|  | import androidx.compose.ui.res.stringResource | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
| import androidx.compose.ui.unit.dp | import androidx.compose.ui.unit.dp | ||||||
| import androidx.fragment.app.FragmentActivity | import androidx.fragment.app.FragmentActivity | ||||||
| @@ -27,6 +31,7 @@ import coil.compose.AsyncImage | |||||||
| import org.schabi.newpipe.R | import org.schabi.newpipe.R | ||||||
| import org.schabi.newpipe.extractor.comments.CommentsInfoItem | import org.schabi.newpipe.extractor.comments.CommentsInfoItem | ||||||
| import org.schabi.newpipe.extractor.stream.Description | import org.schabi.newpipe.extractor.stream.Description | ||||||
|  | import org.schabi.newpipe.ui.theme.AppTheme | ||||||
| import org.schabi.newpipe.util.NavigationHelper | import org.schabi.newpipe.util.NavigationHelper | ||||||
| import org.schabi.newpipe.util.image.ImageStrategy | import org.schabi.newpipe.util.image.ImageStrategy | ||||||
|  |  | ||||||
| @@ -34,51 +39,93 @@ import org.schabi.newpipe.util.image.ImageStrategy | |||||||
| fun Comment(comment: CommentsInfoItem) { | fun Comment(comment: CommentsInfoItem) { | ||||||
|     val context = LocalContext.current |     val context = LocalContext.current | ||||||
|  |  | ||||||
|     Row(modifier = Modifier.padding(all = 8.dp)) { |     Surface(color = MaterialTheme.colorScheme.background) { | ||||||
|         if (ImageStrategy.shouldLoadImages()) { |         Row(modifier = Modifier.padding(all = 8.dp)) { | ||||||
|             AsyncImage( |             if (ImageStrategy.shouldLoadImages()) { | ||||||
|                 model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars), |                 AsyncImage( | ||||||
|                 contentDescription = null, |                     model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars), | ||||||
|                 placeholder = painterResource(R.drawable.placeholder_person), |                     contentDescription = null, | ||||||
|                 error = painterResource(R.drawable.placeholder_person), |                     placeholder = painterResource(R.drawable.placeholder_person), | ||||||
|                 modifier = Modifier |                     error = painterResource(R.drawable.placeholder_person), | ||||||
|                     .size(42.dp) |                     modifier = Modifier | ||||||
|                     .clip(CircleShape) |                         .size(42.dp) | ||||||
|                     .clickable { |                         .clip(CircleShape) | ||||||
|                         NavigationHelper.openCommentAuthorIfPresent(context as FragmentActivity, comment) |                         .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 }, | ||||||
|  |                 verticalArrangement = Arrangement.spacedBy(4.dp) | ||||||
|  |             ) { | ||||||
|  |                 Text( | ||||||
|  |                     text = comment.uploaderName, | ||||||
|  |                     color = MaterialTheme.colorScheme.secondary | ||||||
|  |                 ) | ||||||
|  |  | ||||||
|  |                 Text( | ||||||
|  |                     text = comment.commentText.content, | ||||||
|  |                     // 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, | ||||||
|  |                     style = MaterialTheme.typography.bodyMedium | ||||||
|  |                 ) | ||||||
|  |  | ||||||
|  |                 Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { | ||||||
|  |                     Image( | ||||||
|  |                         painter = painterResource(R.drawable.ic_thumb_up), | ||||||
|  |                         contentDescription = stringResource(R.string.detail_likes_img_view_description) | ||||||
|  |                     ) | ||||||
|  |                     Text(text = comment.likeCount.toString()) | ||||||
|  |  | ||||||
|  |                     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) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         Spacer(modifier = Modifier.width(8.dp)) |         // TODO: Add support for comment replies | ||||||
|  |  | ||||||
|         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 | @Preview( | ||||||
|  |     name = "Light mode", | ||||||
|  |     uiMode = Configuration.UI_MODE_NIGHT_NO | ||||||
|  | ) | ||||||
|  | @Preview( | ||||||
|  |     name = "Dark mode", | ||||||
|  |     uiMode = Configuration.UI_MODE_NIGHT_YES | ||||||
|  | ) | ||||||
| @Composable | @Composable | ||||||
| fun CommentPreview() { | fun CommentPreview() { | ||||||
|     val comment = CommentsInfoItem(1, "", "") |     val comment = CommentsInfoItem(1, "", "") | ||||||
|     comment.commentText = Description("Hello world!", Description.PLAIN_TEXT) |     comment.commentText = Description("Hello world!", Description.PLAIN_TEXT) | ||||||
|     comment.uploaderName = "Test" |     comment.uploaderName = "Test" | ||||||
|  |     comment.likeCount = 100 | ||||||
|  |     comment.isHeartedByUploader = true | ||||||
|  |     comment.isPinned = true | ||||||
|  |  | ||||||
|     Comment(comment) |     AppTheme { | ||||||
|  |         Comment(comment) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage | |||||||
| import org.schabi.newpipe.extractor.comments.CommentsInfoItem | import org.schabi.newpipe.extractor.comments.CommentsInfoItem | ||||||
| import org.schabi.newpipe.fragments.list.BaseListInfoFragment | import org.schabi.newpipe.fragments.list.BaseListInfoFragment | ||||||
| import org.schabi.newpipe.info_list.ItemViewMode | import org.schabi.newpipe.info_list.ItemViewMode | ||||||
|  | import org.schabi.newpipe.ui.theme.AppTheme | ||||||
| import org.schabi.newpipe.util.ExtractorHelper | import org.schabi.newpipe.util.ExtractorHelper | ||||||
| import org.schabi.newpipe.util.Localization | import org.schabi.newpipe.util.Localization | ||||||
| import java.util.Queue | import java.util.Queue | ||||||
| @@ -50,7 +51,9 @@ class CommentRepliesFragment() : BaseListInfoFragment<CommentsInfoItem, CommentR | |||||||
|         return Supplier { |         return Supplier { | ||||||
|             ComposeView(requireContext()).apply { |             ComposeView(requireContext()).apply { | ||||||
|                 setContent { |                 setContent { | ||||||
|                     CommentRepliesHeader(commentsInfoItem, disposables) |                     AppTheme { | ||||||
|  |                         CommentRepliesHeader(commentsInfoItem, disposables) | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package org.schabi.newpipe.fragments.list.comments | package org.schabi.newpipe.fragments.list.comments | ||||||
|  |  | ||||||
|  | import android.content.res.Configuration | ||||||
| import android.widget.TextView | import android.widget.TextView | ||||||
| import androidx.compose.foundation.Image | import androidx.compose.foundation.Image | ||||||
| import androidx.compose.foundation.clickable | import androidx.compose.foundation.clickable | ||||||
| @@ -12,7 +13,9 @@ import androidx.compose.foundation.layout.padding | |||||||
| import androidx.compose.foundation.layout.size | import androidx.compose.foundation.layout.size | ||||||
| import androidx.compose.foundation.layout.width | import androidx.compose.foundation.layout.width | ||||||
| import androidx.compose.foundation.shape.CircleShape | import androidx.compose.foundation.shape.CircleShape | ||||||
| import androidx.compose.material.Text | import androidx.compose.material3.MaterialTheme | ||||||
|  | import androidx.compose.material3.Surface | ||||||
|  | import androidx.compose.material3.Text | ||||||
| import androidx.compose.runtime.Composable | import androidx.compose.runtime.Composable | ||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| @@ -31,6 +34,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable | |||||||
| import org.schabi.newpipe.R | import org.schabi.newpipe.R | ||||||
| import org.schabi.newpipe.extractor.comments.CommentsInfoItem | import org.schabi.newpipe.extractor.comments.CommentsInfoItem | ||||||
| import org.schabi.newpipe.extractor.stream.Description | 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.Localization | ||||||
| import org.schabi.newpipe.util.NavigationHelper | import org.schabi.newpipe.util.NavigationHelper | ||||||
| import org.schabi.newpipe.util.ServiceHelper | import org.schabi.newpipe.util.ServiceHelper | ||||||
| @@ -41,94 +45,105 @@ import org.schabi.newpipe.util.text.TextLinkifier | |||||||
| fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDisposable) { | fun CommentRepliesHeader(comment: CommentsInfoItem, disposables: CompositeDisposable) { | ||||||
|     val context = LocalContext.current |     val context = LocalContext.current | ||||||
|  |  | ||||||
|     Column(modifier = Modifier.padding(all = 8.dp)) { |     Surface(color = MaterialTheme.colorScheme.background) { | ||||||
|         Row( |         Column(modifier = Modifier.padding(all = 8.dp)) { | ||||||
|             modifier = Modifier.fillMaxWidth(), |  | ||||||
|             verticalAlignment = Alignment.CenterVertically |  | ||||||
|         ) { |  | ||||||
|             Row( |             Row( | ||||||
|                 modifier = Modifier |                 modifier = Modifier.fillMaxWidth(), | ||||||
|                     .padding(top = 8.dp, bottom = 8.dp, end = 8.dp) |  | ||||||
|                     .clickable { |  | ||||||
|                         NavigationHelper.openCommentAuthorIfPresent( |  | ||||||
|                             context as FragmentActivity, |  | ||||||
|                             comment |  | ||||||
|                         ) |  | ||||||
|                     }, |  | ||||||
|                 verticalAlignment = Alignment.CenterVertically |                 verticalAlignment = Alignment.CenterVertically | ||||||
|             ) { |             ) { | ||||||
|                 if (ImageStrategy.shouldLoadImages()) { |                 Row( | ||||||
|                     AsyncImage( |                     modifier = Modifier | ||||||
|                         model = ImageStrategy.choosePreferredImage(comment.uploaderAvatars), |                         .padding(top = 8.dp, bottom = 8.dp, end = 8.dp) | ||||||
|                         contentDescription = null, |                         .clickable { | ||||||
|                         placeholder = painterResource(R.drawable.placeholder_person), |                             NavigationHelper.openCommentAuthorIfPresent( | ||||||
|                         error = painterResource(R.drawable.placeholder_person), |                                 context as FragmentActivity, | ||||||
|                         modifier = Modifier |                                 comment | ||||||
|                             .size(42.dp) |                             ) | ||||||
|                             .clip(CircleShape) |                         }, | ||||||
|                     ) |                     verticalAlignment = Alignment.CenterVertically | ||||||
|  |                 ) { | ||||||
|  |                     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( | ||||||
|  |                             color = MaterialTheme.colorScheme.secondary, | ||||||
|  |                             style = MaterialTheme.typography.bodySmall, | ||||||
|  |                             text = Localization.relativeTimeOrTextual( | ||||||
|  |                                 context, comment.uploadDate, comment.textualUploadDate | ||||||
|  |                             ) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 Spacer(modifier = Modifier.width(8.dp)) |                 Spacer(modifier = Modifier.weight(1f)) | ||||||
|  |  | ||||||
|                 Column { |                 Row( | ||||||
|                     Text(text = comment.uploaderName) |                     horizontalArrangement = Arrangement.spacedBy(8.dp), | ||||||
|  |                     verticalAlignment = Alignment.CenterVertically | ||||||
|                     Text( |                 ) { | ||||||
|                         text = Localization.relativeTimeOrTextual( |                     Image( | ||||||
|                             context, comment.uploadDate, comment.textualUploadDate |                         painter = painterResource(R.drawable.ic_thumb_up), | ||||||
|                         ) |                         contentDescription = stringResource(R.string.detail_likes_img_view_description) | ||||||
|                     ) |                     ) | ||||||
|  |                     Text(text = comment.likeCount.toString()) | ||||||
|  |  | ||||||
|  |                     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) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             Spacer(modifier = Modifier.weight(1f)) |             AndroidView( | ||||||
|  |                 factory = { context -> | ||||||
|             Row( |                     TextView(context).apply { | ||||||
|                 horizontalArrangement = Arrangement.spacedBy(8.dp), |                         movementMethod = LinkMovementMethodCompat.getInstance() | ||||||
|                 verticalAlignment = Alignment.CenterVertically |                     } | ||||||
|             ) { |                 }, | ||||||
|                 Image( |                 update = { view -> | ||||||
|                     painter = painterResource(R.drawable.ic_thumb_up), |                     // setup comment content | ||||||
|                     contentDescription = stringResource(R.string.detail_likes_img_view_description) |                     TextLinkifier.fromDescription( | ||||||
|                 ) |                         view, comment.commentText, HtmlCompat.FROM_HTML_MODE_LEGACY, | ||||||
|                 Text(text = comment.likeCount.toString()) |                         ServiceHelper.getServiceById(comment.serviceId), comment.url, disposables, | ||||||
|  |                         null | ||||||
|                 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 | @Preview( | ||||||
|  |     name = "Light mode", | ||||||
|  |     uiMode = Configuration.UI_MODE_NIGHT_NO | ||||||
|  | ) | ||||||
|  | @Preview( | ||||||
|  |     name = "Dark mode", | ||||||
|  |     uiMode = Configuration.UI_MODE_NIGHT_YES | ||||||
|  | ) | ||||||
| @Composable | @Composable | ||||||
| fun CommentRepliesHeaderPreview() { | fun CommentRepliesHeaderPreview() { | ||||||
|     val disposables = CompositeDisposable() |     val disposables = CompositeDisposable() | ||||||
| @@ -140,5 +155,7 @@ fun CommentRepliesHeaderPreview() { | |||||||
|     comment.isPinned = true |     comment.isPinned = true | ||||||
|     comment.isHeartedByUploader = true |     comment.isHeartedByUploader = true | ||||||
|  |  | ||||||
|     CommentRepliesHeader(comment, disposables) |     AppTheme { | ||||||
|  |         CommentRepliesHeader(comment, disposables) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Isira Seneviratne
					Isira Seneviratne