mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-02-08 23:20:10 +00:00
Replace CommentRepliesFragment with bottom sheet composable, improve previews
This commit is contained in:
parent
93310955f2
commit
b9dd7078ad
@ -44,7 +44,6 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@ -52,7 +51,6 @@ import androidx.core.app.ActivityCompat;
|
|||||||
import androidx.core.view.GravityCompat;
|
import androidx.core.view.GravityCompat;
|
||||||
import androidx.drawerlayout.widget.DrawerLayout;
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentContainerView;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
@ -71,7 +69,6 @@ import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
|
|||||||
import org.schabi.newpipe.fragments.BackPressable;
|
import org.schabi.newpipe.fragments.BackPressable;
|
||||||
import org.schabi.newpipe.fragments.MainFragment;
|
import org.schabi.newpipe.fragments.MainFragment;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment;
|
|
||||||
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
||||||
import org.schabi.newpipe.local.feed.notifications.NotificationWorker;
|
import org.schabi.newpipe.local.feed.notifications.NotificationWorker;
|
||||||
import org.schabi.newpipe.player.Player;
|
import org.schabi.newpipe.player.Player;
|
||||||
@ -557,33 +554,22 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
// interacts with a fragment inside fragment_holder so all back presses should be
|
// interacts with a fragment inside fragment_holder so all back presses should be
|
||||||
// handled by it
|
// handled by it
|
||||||
if (bottomSheetHiddenOrCollapsed()) {
|
if (bottomSheetHiddenOrCollapsed()) {
|
||||||
final FragmentManager fm = getSupportFragmentManager();
|
final var fm = getSupportFragmentManager();
|
||||||
final Fragment fragment = fm.findFragmentById(R.id.fragment_holder);
|
final var fragment = fm.findFragmentById(R.id.fragment_holder);
|
||||||
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
|
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
|
||||||
// delegate the back press to it
|
// delegate the back press to it
|
||||||
if (fragment instanceof BackPressable) {
|
if (fragment instanceof BackPressable backPressable && backPressable.onBackPressed()) {
|
||||||
if (((BackPressable) fragment).onBackPressed()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (fragment instanceof CommentRepliesFragment) {
|
|
||||||
// expand DetailsFragment if CommentRepliesFragment was opened
|
|
||||||
// to show the top level comments again
|
|
||||||
// Expand DetailsFragment if CommentRepliesFragment was opened
|
|
||||||
// and no other CommentRepliesFragments are on top of the back stack
|
|
||||||
// to show the top level comments again.
|
|
||||||
openDetailFragmentFromCommentReplies(fm, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
final Fragment fragmentPlayer = getSupportFragmentManager()
|
final var fragmentPlayer = getSupportFragmentManager()
|
||||||
.findFragmentById(R.id.fragment_player_holder);
|
.findFragmentById(R.id.fragment_player_holder);
|
||||||
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
|
// If current fragment implements BackPressable (i.e. can/wanna handle back press)
|
||||||
// delegate the back press to it
|
// delegate the back press to it
|
||||||
if (fragmentPlayer instanceof BackPressable) {
|
if (fragmentPlayer instanceof BackPressable backPressable
|
||||||
if (!((BackPressable) fragmentPlayer).onBackPressed()) {
|
&& !backPressable.onBackPressed()) {
|
||||||
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder)
|
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder)
|
||||||
.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -647,15 +633,9 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
private void onHomeButtonPressed() {
|
private void onHomeButtonPressed() {
|
||||||
final FragmentManager fm = getSupportFragmentManager();
|
final var fm = getSupportFragmentManager();
|
||||||
final Fragment fragment = fm.findFragmentById(R.id.fragment_holder);
|
|
||||||
|
|
||||||
if (fragment instanceof CommentRepliesFragment) {
|
if (!NavigationHelper.tryGotoSearchFragment(fm)) {
|
||||||
// Expand DetailsFragment if CommentRepliesFragment was opened
|
|
||||||
// and no other CommentRepliesFragments are on top of the back stack
|
|
||||||
// to show the top level comments again.
|
|
||||||
openDetailFragmentFromCommentReplies(fm, true);
|
|
||||||
} else if (!NavigationHelper.tryGotoSearchFragment(fm)) {
|
|
||||||
// If search fragment wasn't found in the backstack go to the main fragment
|
// If search fragment wasn't found in the backstack go to the main fragment
|
||||||
NavigationHelper.gotoMainFragment(fm);
|
NavigationHelper.gotoMainFragment(fm);
|
||||||
}
|
}
|
||||||
@ -853,67 +833,6 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openDetailFragmentFromCommentReplies(
|
|
||||||
@NonNull final FragmentManager fm,
|
|
||||||
final boolean popBackStack
|
|
||||||
) {
|
|
||||||
// obtain the name of the fragment under the replies fragment that's going to be popped
|
|
||||||
@Nullable final String fragmentUnderEntryName;
|
|
||||||
if (fm.getBackStackEntryCount() < 2) {
|
|
||||||
fragmentUnderEntryName = null;
|
|
||||||
} else {
|
|
||||||
fragmentUnderEntryName = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 2)
|
|
||||||
.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the root comment is the comment for which the user opened the replies page
|
|
||||||
final var repliesFragment = (CommentRepliesFragment)
|
|
||||||
fm.findFragmentByTag(CommentRepliesFragment.TAG);
|
|
||||||
final var rootComment = repliesFragment == null ? null : repliesFragment.getComment();
|
|
||||||
|
|
||||||
// sometimes this function pops the backstack, other times it's handled by the system
|
|
||||||
if (popBackStack) {
|
|
||||||
fm.popBackStackImmediate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// only expand the bottom sheet back if there are no more nested comment replies fragments
|
|
||||||
// stacked under the one that is currently being popped
|
|
||||||
if (CommentRepliesFragment.TAG.equals(fragmentUnderEntryName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final BottomSheetBehavior<FragmentContainerView> behavior = BottomSheetBehavior
|
|
||||||
.from(mainBinding.fragmentPlayerHolder);
|
|
||||||
// do not return to the comment if the details fragment was closed
|
|
||||||
if (behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scroll to the root comment once the bottom sheet expansion animation is finished
|
|
||||||
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
|
||||||
@Override
|
|
||||||
public void onStateChanged(@NonNull final View bottomSheet,
|
|
||||||
final int newState) {
|
|
||||||
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
|
||||||
final Fragment detailFragment = fm.findFragmentById(
|
|
||||||
R.id.fragment_player_holder);
|
|
||||||
if (detailFragment instanceof VideoDetailFragment && rootComment != null) {
|
|
||||||
// should always be the case
|
|
||||||
((VideoDetailFragment) detailFragment).scrollToComment(rootComment);
|
|
||||||
}
|
|
||||||
behavior.removeBottomSheetCallback(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSlide(@NonNull final View bottomSheet, final float slideOffset) {
|
|
||||||
// not needed, listener is removed once the sheet is expanded
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean bottomSheetHiddenOrCollapsed() {
|
private boolean bottomSheetHiddenOrCollapsed() {
|
||||||
final BottomSheetBehavior<FrameLayout> bottomSheetBehavior =
|
final BottomSheetBehavior<FrameLayout> bottomSheetBehavior =
|
||||||
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder);
|
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder);
|
||||||
|
@ -12,7 +12,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.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@ -38,6 +40,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.extractor.Page
|
import org.schabi.newpipe.extractor.Page
|
||||||
@ -60,10 +64,12 @@ fun rememberParsedText(commentText: Description): AnnotatedString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Comment(comment: CommentsInfoItem) {
|
fun Comment(comment: CommentsInfoItem) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var isExpanded by rememberSaveable { mutableStateOf(false) }
|
var isExpanded by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var showReplies by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
Surface(color = MaterialTheme.colorScheme.background) {
|
Surface(color = MaterialTheme.colorScheme.background) {
|
||||||
Row(
|
Row(
|
||||||
@ -139,22 +145,29 @@ fun Comment(comment: CommentsInfoItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (comment.replies != null) {
|
if (comment.replies != null) {
|
||||||
TextButton(onClick = {
|
TextButton(onClick = { showReplies = true }) {
|
||||||
NavigationHelper.openCommentRepliesFragment(
|
val text = pluralStringResource(
|
||||||
context as FragmentActivity, comment
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
text = pluralStringResource(
|
|
||||||
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
|
R.plurals.replies, comment.replyCount, comment.replyCount.toString()
|
||||||
)
|
)
|
||||||
)
|
Text(text = text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showReplies) {
|
||||||
|
ModalBottomSheet(onDismissRequest = { showReplies = false }) {
|
||||||
|
val flow = remember(comment) {
|
||||||
|
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
|
||||||
|
CommentsSource(comment.serviceId, comment.url, comment.replies)
|
||||||
|
}.flow
|
||||||
|
}
|
||||||
|
|
||||||
|
CommentSection(parentComment = comment, flow = flow)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CommentsInfoItem(
|
fun CommentsInfoItem(
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
package org.schabi.newpipe.fragments.list.comments
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.paging.Pager
|
|
||||||
import androidx.paging.PagingConfig
|
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem
|
|
||||||
import org.schabi.newpipe.ktx.serializable
|
|
||||||
import org.schabi.newpipe.ui.theme.AppTheme
|
|
||||||
import org.schabi.newpipe.util.Localization
|
|
||||||
|
|
||||||
class CommentRepliesFragment : Fragment() {
|
|
||||||
lateinit var comment: CommentsInfoItem
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
comment = requireArguments().serializable<CommentsInfoItem>(COMMENT_KEY)!!
|
|
||||||
|
|
||||||
val activity = requireActivity() as AppCompatActivity
|
|
||||||
val bar = activity.supportActionBar!!
|
|
||||||
bar.setDisplayShowTitleEnabled(true)
|
|
||||||
bar.title = Localization.replyCount(activity, comment.replyCount)
|
|
||||||
|
|
||||||
return ComposeView(activity).apply {
|
|
||||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
|
||||||
setContent {
|
|
||||||
val flow = remember(comment) {
|
|
||||||
Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) {
|
|
||||||
CommentsSource(comment.serviceId, comment.url, comment.replies)
|
|
||||||
}.flow
|
|
||||||
}
|
|
||||||
|
|
||||||
AppTheme {
|
|
||||||
CommentSection(parentComment = comment, flow = flow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField
|
|
||||||
val TAG = CommentRepliesFragment::class.simpleName!!
|
|
||||||
|
|
||||||
const val COMMENT_KEY = "comment"
|
|
||||||
}
|
|
||||||
}
|
|
@ -45,7 +45,7 @@ fun CommentSection(
|
|||||||
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
@Composable
|
@Composable
|
||||||
private fun CommentSectionPreview() {
|
private fun CommentSectionPreview() {
|
||||||
val comments = (0..100).map {
|
val comments = (1..100).map {
|
||||||
CommentsInfoItem(
|
CommentsInfoItem(
|
||||||
commentText = Description("Comment $it", Description.PLAIN_TEXT),
|
commentText = Description("Comment $it", Description.PLAIN_TEXT),
|
||||||
uploaderName = "Test"
|
uploaderName = "Test"
|
||||||
@ -69,7 +69,7 @@ private fun CommentRepliesPreview() {
|
|||||||
isPinned = true,
|
isPinned = true,
|
||||||
isHeartedByUploader = true
|
isHeartedByUploader = true
|
||||||
)
|
)
|
||||||
val replies = (0..100).map {
|
val replies = (1..100).map {
|
||||||
CommentsInfoItem(
|
CommentsInfoItem(
|
||||||
commentText = Description("Reply $it", Description.PLAIN_TEXT),
|
commentText = Description("Reply $it", Description.PLAIN_TEXT),
|
||||||
uploaderName = "Test"
|
uploaderName = "Test"
|
||||||
|
@ -9,7 +9,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -46,7 +45,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream;
|
|||||||
import org.schabi.newpipe.fragments.MainFragment;
|
import org.schabi.newpipe.fragments.MainFragment;
|
||||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||||
import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment;
|
|
||||||
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||||
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
|
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
|
||||||
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
import org.schabi.newpipe.fragments.list.search.SearchFragment;
|
||||||
@ -502,18 +500,6 @@ public final class NavigationHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openCommentRepliesFragment(@NonNull final FragmentActivity activity,
|
|
||||||
@NonNull final CommentsInfoItem comment) {
|
|
||||||
final var bundle = new Bundle();
|
|
||||||
bundle.putSerializable(CommentRepliesFragment.COMMENT_KEY, comment);
|
|
||||||
|
|
||||||
defaultTransaction(activity.getSupportFragmentManager())
|
|
||||||
.replace(R.id.fragment_holder, CommentRepliesFragment.class, bundle,
|
|
||||||
CommentRepliesFragment.TAG)
|
|
||||||
.addToBackStack(CommentRepliesFragment.TAG)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openPlaylistFragment(final FragmentManager fragmentManager,
|
public static void openPlaylistFragment(final FragmentManager fragmentManager,
|
||||||
final int serviceId, final String url,
|
final int serviceId, final String url,
|
||||||
@NonNull final String name) {
|
@NonNull final String name) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user