Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import java.io.Serializable;

class StackItem implements Serializable {
public class StackItem implements Serializable {
private final int serviceId;
private String url;
private String title;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.material3.Surface
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.compose.content
import androidx.lifecycle.viewmodel.compose.viewModel
import org.schabi.newpipe.ui.components.video.comment.CommentSection
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.KEY_URL

class CommentsFragment : Fragment() {
override fun onCreateView(
Expand All @@ -20,15 +18,8 @@ class CommentsFragment : Fragment() {
) = content {
AppTheme {
Surface {
CommentSection()
CommentSection(viewModel(requireParentFragment()))
}
}
}

companion object {
@JvmStatic
fun getInstance(serviceId: Int, url: String?) = CommentsFragment().apply {
arguments = bundleOf(KEY_SERVICE_ID to serviceId, KEY_URL to url)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.material3.Surface
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.compose.content
import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.ktx.serializable
import androidx.lifecycle.viewmodel.compose.viewModel
import org.schabi.newpipe.ui.components.video.RelatedItems
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.KEY_INFO

class RelatedItemsFragment : Fragment() {
override fun onCreateView(
Expand All @@ -21,15 +18,8 @@ class RelatedItemsFragment : Fragment() {
) = content {
AppTheme {
Surface {
RelatedItems(requireArguments().serializable<StreamInfo>(KEY_INFO)!!)
RelatedItems(viewModel(requireParentFragment()))
}
}
}

companion object {
@JvmStatic
fun getInstance(info: StreamInfo) = RelatedItemsFragment().apply {
arguments = bundleOf(KEY_INFO to info)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,35 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.preference.PreferenceManager
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.extractor.stream.StreamType
import org.schabi.newpipe.info_list.ItemViewMode
import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.components.items.ItemList
import org.schabi.newpipe.ui.components.items.stream.StreamInfoItem
import org.schabi.newpipe.ui.emptystate.EmptyStateComposable
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.NO_SERVICE_ID
import org.schabi.newpipe.viewmodels.VideoDetailViewModel
import org.schabi.newpipe.viewmodels.util.Resource

@Composable
fun RelatedItems(info: StreamInfo) {
fun RelatedItems(viewModel: VideoDetailViewModel) {
val streamState by viewModel.streamState.collectAsStateWithLifecycle()

when (val state = streamState) {
is Resource.Loading -> LoadingIndicator()
is Resource.Success -> RelatedItems(state.data)
is Resource.Error -> TODO()
}
}

@Composable
private fun RelatedItems(info: StreamInfo) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(LocalContext.current)
val key = stringResource(R.string.auto_queue_key)
// TODO: AndroidX DataStore might be a better option.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ import org.schabi.newpipe.util.image.ImageStrategy

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
fun Comment(
comment: CommentsInfoItem,
uploaderAvatarUrl: String? = null,
onCommentAuthorOpened: (() -> Unit)? = null,
) {
val context = LocalContext.current
var isExpanded by rememberSaveable { mutableStateOf(false) }
var showReplies by rememberSaveable { mutableStateOf(false) }
Expand All @@ -82,7 +86,7 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
.clip(CircleShape)
.clickable {
NavigationHelper.openCommentAuthorIfPresent(context, comment)
onCommentAuthorOpened()
onCommentAuthorOpened?.invoke()
}
)

Expand Down Expand Up @@ -161,17 +165,31 @@ fun Comment(comment: CommentsInfoItem, onCommentAuthorOpened: () -> Unit) {
}

if (comment.replies != null) {
// reduce LocalMinimumInteractiveComponentSize from 48dp to 44dp to slightly
// reduce the button margin (which is still clickable but not visible)
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 44.dp) {
TextButton(
onClick = { showReplies = true },
modifier = Modifier.padding(end = 2.dp)
) {
val text = pluralStringResource(
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
Row(verticalAlignment = Alignment.CenterVertically) {
if (comment.hasCreatorReply()) {
AsyncImage(
model = uploaderAvatarUrl,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_person),
error = painterResource(R.drawable.placeholder_person),
modifier = Modifier
.size(30.dp)
.clip(CircleShape)
)
Text(text = text)
}

// reduce LocalMinimumInteractiveComponentSize from 48dp to 44dp to slightly
// reduce the button margin (which is still clickable but not visible)
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 44.dp) {
TextButton(
onClick = { showReplies = true },
modifier = Modifier.padding(end = 2.dp)
) {
val text = pluralStringResource(
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
)
Text(text = text)
}
}
}
}
Expand Down Expand Up @@ -200,6 +218,7 @@ fun CommentsInfoItem(
isPinned: Boolean = false,
replies: Page? = null,
replyCount: Int = 0,
hasCreatorReply: Boolean = false,
) = CommentsInfoItem(serviceId, url, name).apply {
this.commentText = commentText
this.uploaderName = uploaderName
Expand All @@ -209,6 +228,7 @@ fun CommentsInfoItem(
this.isPinned = isPinned
this.replies = replies
this.replyCount = replyCount
setCreatorReply(hasCreatorReply)
}

private class CommentPreviewProvider : CollectionPreviewParameterProvider<CommentsInfoItem>(
Expand Down Expand Up @@ -247,7 +267,8 @@ private class CommentPreviewProvider : CollectionPreviewParameterProvider<Commen
isPinned = false,
isHeartedByUploader = false,
replies = Page(""),
replyCount = 4283
replyCount = 4283,
hasCreatorReply = true,
),
)
)
Expand All @@ -260,7 +281,7 @@ private fun CommentPreview(
) {
AppTheme {
Surface {
Comment(commentsInfoItem) {}
Comment(commentsInfoItem)
}
}
}
Expand All @@ -272,7 +293,7 @@ private fun CommentListPreview() {
Surface {
Column {
for (comment in CommentPreviewProvider().values) {
Comment(comment) {}
Comment(comment)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import org.schabi.newpipe.ui.theme.AppTheme
fun CommentRepliesDialog(
parentComment: CommentsInfoItem,
onDismissRequest: () -> Unit,
onCommentAuthorOpened: () -> Unit,
onCommentAuthorOpened: (() -> Unit)?,
) {
val coroutineScope = rememberCoroutineScope()
val commentsFlow = remember {
Expand All @@ -66,7 +66,7 @@ private fun CommentRepliesDialog(
parentComment: CommentsInfoItem,
commentsFlow: Flow<PagingData<CommentsInfoItem>>,
onDismissRequest: () -> Unit,
onCommentAuthorOpened: () -> Unit,
onCommentAuthorOpened: (() -> Unit)? = null,
) {
val comments = commentsFlow.collectAsLazyPagingItems()
val nestedScrollInterop = rememberNestedScrollInteropConnection()
Expand All @@ -76,7 +76,7 @@ private fun CommentRepliesDialog(
val sheetState = rememberModalBottomSheetState()
val nestedOnCommentAuthorOpened: () -> Unit = {
// also partialExpand any parent dialog
onCommentAuthorOpened()
onCommentAuthorOpened?.invoke()
coroutineScope.launch {
sheetState.partialExpand()
}
Expand Down Expand Up @@ -148,10 +148,7 @@ private fun CommentRepliesDialog(
}
} else {
items(comments.itemCount) {
Comment(
comment = comments[it]!!,
onCommentAuthorOpened = nestedOnCommentAuthorOpened,
)
Comment(comments[it]!!, onCommentAuthorOpened = nestedOnCommentAuthorOpened)
}
}
}
Expand Down Expand Up @@ -182,6 +179,6 @@ private fun CommentRepliesDialogPreview() {
val flow = flowOf(PagingData.from(replies))

AppTheme {
CommentRepliesDialog(comment, flow, onDismissRequest = {}, onCommentAuthorOpened = {})
CommentRepliesDialog(comment, flow, onDismissRequest = {})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
Expand All @@ -34,18 +33,28 @@ import org.schabi.newpipe.ui.components.common.LoadingIndicator
import org.schabi.newpipe.ui.emptystate.EmptyStateComposable
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.viewmodels.CommentsViewModel
import org.schabi.newpipe.util.image.ImageStrategy
import org.schabi.newpipe.viewmodels.VideoDetailViewModel
import org.schabi.newpipe.viewmodels.util.Resource

@Composable
fun CommentSection(commentsViewModel: CommentsViewModel = viewModel()) {
val state by commentsViewModel.uiState.collectAsStateWithLifecycle()
CommentSection(state, commentsViewModel.comments)
fun CommentSection(viewModel: VideoDetailViewModel) {
val streamState by viewModel.streamState.collectAsStateWithLifecycle()
val commentState by viewModel.commentState.collectAsStateWithLifecycle()
val avatars = (streamState as? Resource.Success)?.data?.uploaderAvatars.orEmpty()
val uploaderAvatarUrl = ImageStrategy.choosePreferredImage(avatars)

CommentSection(
commentState,
uploaderAvatarUrl,
viewModel.comments,
)
}

@Composable
private fun CommentSection(
uiState: Resource<CommentInfo>,
uploaderAvatarUrl: String? = null,
commentsFlow: Flow<PagingData<CommentsInfoItem>>
) {
val comments = commentsFlow.collectAsLazyPagingItems()
Expand Down Expand Up @@ -123,7 +132,7 @@ private fun CommentSection(

else -> {
items(comments.itemCount) {
Comment(comment = comments[it]!!) {}
Comment(comments[it]!!, uploaderAvatarUrl)
}
}
}
Expand Down Expand Up @@ -156,7 +165,7 @@ private fun CommentSection(
private fun CommentSectionLoadingPreview() {
AppTheme {
Surface {
CommentSection(uiState = Resource.Loading, commentsFlow = flowOf())
CommentSection(Resource.Loading, commentsFlow = flowOf())
}
}
}
Expand Down Expand Up @@ -203,7 +212,7 @@ private fun CommentSectionSuccessPreview() {
private fun CommentSectionErrorPreview() {
AppTheme {
Surface {
CommentSection(uiState = Resource.Error(RuntimeException()), commentsFlow = flowOf())
CommentSection(Resource.Error(RuntimeException()), commentsFlow = flowOf())
}
}
}
4 changes: 2 additions & 2 deletions app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static android.content.Context.INPUT_SERVICE;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.UiModeManager;
import android.content.Context;
import android.content.pm.PackageManager;
Expand All @@ -21,7 +22,6 @@

import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;

Expand Down Expand Up @@ -286,7 +286,7 @@ public static boolean isLandscape(final Context context) {
.getDisplayMetrics().widthPixels;
}

public static boolean isInMultiWindow(final AppCompatActivity activity) {
public static boolean isInMultiWindow(final Activity activity) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInMultiWindowMode();
}

Expand Down
Loading