1
0
mirror of https://github.com/TeamNewPipe/NewPipe.git synced 2024-11-22 11:02:35 +01:00

Added missing comment features, fixed theming

This commit is contained in:
Isira Seneviratne 2024-05-17 10:17:09 +05:30
parent 8ce9a7e43c
commit 56c80ce6dd
4 changed files with 181 additions and 114 deletions

View File

@ -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) {

View File

@ -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)
}
} }

View File

@ -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)
}
} }
} }
} }

View File

@ -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)
}
} }