mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2024-11-22 11:02:35 +01:00
Handle no comments and comments disabled scenarios
This commit is contained in:
parent
b092fe2c76
commit
5e7e14ee4d
@ -881,8 +881,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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user