1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-07-01 07:04:48 +00:00

Handle no comments and comments disabled scenarios

This commit is contained in:
Isira Seneviratne 2024-06-23 10:53:22 +05:30
parent e9a4ae8d26
commit 7f7faac8cf
4 changed files with 154 additions and 95 deletions

View File

@ -882,8 +882,7 @@ public final class VideoDetailFragment
tabContentDescriptions.clear();
if (shouldShowComments()) {
pageAdapter.addFragment(
CommentsFragment.getInstance(serviceId, url), COMMENTS_TAB_TAG);
pageAdapter.addFragment(CommentsFragment.getInstance(serviceId, url), COMMENTS_TAB_TAG);
tabIcons.add(R.drawable.ic_comment);
tabContentDescriptions.add(R.string.comments_tab_description);
}

View File

@ -69,85 +69,83 @@ fun Comment(comment: CommentsInfoItem) {
var isExpanded by rememberSaveable { mutableStateOf(false) }
var showReplies by rememberSaveable { mutableStateOf(false) }
Surface(color = MaterialTheme.colorScheme.background) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { isExpanded = !isExpanded }
.padding(all = 8.dp),
horizontalArrangement = Arrangement.spacedBy(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
)
}
)
}
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
if (comment.isPinned) {
Image(
painter = painterResource(R.drawable.ic_pin),
contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { isExpanded = !isExpanded }
.padding(all = 8.dp),
horizontalArrangement = Arrangement.spacedBy(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
)
}
)
}
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)
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
if (comment.isPinned) {
Image(
painter = painterResource(R.drawable.ic_pin),
contentDescription = stringResource(R.string.detail_pinned_comment_view_description)
)
}
Text(
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,
)
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)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
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,
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
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 = Localization.likeCount(context, comment.likeCount))
if (comment.isHeartedByUploader) {
Image(
painter = painterResource(R.drawable.ic_thumb_up),
contentDescription = stringResource(R.string.detail_likes_img_view_description)
painter = painterResource(R.drawable.ic_heart),
contentDescription = stringResource(R.string.detail_heart_img_view_description)
)
Text(text = Localization.likeCount(context, comment.likeCount))
if (comment.isHeartedByUploader) {
Image(
painter = painterResource(R.drawable.ic_heart),
contentDescription = stringResource(R.string.detail_heart_img_view_description)
)
}
}
}
if (comment.replies != null) {
TextButton(onClick = { showReplies = true }) {
val text = pluralStringResource(
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
)
Text(text = text)
}
if (comment.replies != null) {
TextButton(onClick = { showReplies = true }) {
val text = pluralStringResource(
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
)
Text(text = text)
}
}
}
@ -190,7 +188,7 @@ fun CommentsInfoItem(
this.replyCount = replyCount
}
class DescriptionPreviewProvider : PreviewParameterProvider<Description> {
private 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),
@ -214,6 +212,8 @@ private fun CommentPreview(
)
AppTheme {
Comment(comment)
Surface(color = MaterialTheme.colorScheme.background) {
Comment(comment)
}
}
}

View File

@ -1,18 +1,33 @@
package org.schabi.newpipe.fragments.list.comments
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.res.stringResource
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.compose.ui.unit.sp
import androidx.paging.LoadState
import androidx.paging.LoadStates
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import my.nanihadesuka.compose.LazyColumnScrollbar
import my.nanihadesuka.compose.ScrollbarSettings
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
@ -23,38 +38,81 @@ fun CommentSection(
parentComment: CommentsInfoItem? = null,
) {
val replies = flow.collectAsLazyPagingItems()
val listState = rememberLazyListState()
val itemCount by remember { derivedStateOf { replies.itemCount } }
LazyColumnScrollbar(state = listState, settings = ScrollbarSettings.Default) {
LazyColumn(state = listState) {
if (parentComment != null) {
item {
CommentRepliesHeader(comment = parentComment)
HorizontalDivider(thickness = 1.dp)
Surface(color = MaterialTheme.colorScheme.background) {
val refresh = replies.loadState.refresh
if (itemCount == 0 && refresh !is LoadState.Loading) {
NoCommentsMessage((refresh as? LoadState.Error)?.error)
} else {
val listState = rememberLazyListState()
LazyColumnScrollbar(state = listState, settings = ScrollbarSettings.Default) {
LazyColumn(state = listState) {
if (parentComment != null) {
item {
CommentRepliesHeader(comment = parentComment)
HorizontalDivider(thickness = 1.dp)
}
}
items(itemCount) {
Comment(comment = replies[it]!!)
}
}
}
items(replies.itemCount) {
Comment(comment = replies[it]!!)
}
}
}
}
@Composable
private fun NoCommentsMessage(error: Throwable?) {
val message = if (error is CommentsDisabledException) {
R.string.comments_are_disabled
} else {
R.string.no_comments
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "(╯°-°)╯", fontSize = 35.sp)
Text(text = stringResource(id = message), fontSize = 24.sp)
}
}
private class CommentDataProvider : PreviewParameterProvider<PagingData<CommentsInfoItem>> {
private val notLoading = LoadState.NotLoading(true)
override val values = sequenceOf(
// Normal view
PagingData.from(
(1..100).map {
CommentsInfoItem(
commentText = Description("Comment $it", Description.PLAIN_TEXT),
uploaderName = "Test"
)
}
),
// Comments disabled
PagingData.from(
listOf<CommentsInfoItem>(),
LoadStates(LoadState.Error(CommentsDisabledException()), notLoading, notLoading)
),
// No comments
PagingData.from(
listOf<CommentsInfoItem>(),
LoadStates(notLoading, notLoading, notLoading)
)
)
}
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CommentSectionPreview() {
val comments = (1..100).map {
CommentsInfoItem(
commentText = Description("Comment $it", Description.PLAIN_TEXT),
uploaderName = "Test"
)
}
val flow = flowOf(PagingData.from(comments))
private fun CommentSectionPreview(
@PreviewParameter(CommentDataProvider::class) pagingData: PagingData<CommentsInfoItem>
) {
AppTheme {
CommentSection(flow = flow)
CommentSection(flow = flowOf(pagingData))
}
}

View File

@ -25,7 +25,7 @@ class CommentsSource(
.subscribeOn(Schedulers.io())
.map {
if (it.isCommentsDisabled) {
LoadResult.Invalid()
LoadResult.Error(CommentsDisabledException())
} else {
LoadResult.Page(it.relatedItems, null, it.nextPage)
}
@ -34,3 +34,5 @@ class CommentsSource(
override fun getRefreshKey(state: PagingState<Page, CommentsInfoItem>) = null
}
class CommentsDisabledException : RuntimeException()