Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show comment replies #9410

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e201b79
Show comment replies in DialogFragment
xz-dev Sep 25, 2022
703876c
Show initial comment in CommentReplyDialog
xz-dev Sep 25, 2022
8512198
Fix wrong background color for Dark theme for BottomSheetDialogFragment
xz-dev Sep 25, 2022
18f3349
Fix wrong string
xz-dev Sep 25, 2022
5cd7807
Fix comment replies only work at the first time
xz-dev Sep 25, 2022
a8fcccd
Clean code for comment reply
xz-dev Sep 25, 2022
8f8e29c
Add click effect to back button
xz-dev Sep 25, 2022
648e331
Improve bottom sheets design
krlvm Sep 25, 2022
a4cd42b
Fix comment replies pagination
xz-dev Sep 26, 2022
5603e85
Avoid mocked classes are cached
xz-dev Sep 26, 2022
fbd5f24
Auto dismiss commit reply dialog
xz-dev Sep 26, 2022
b91df5e
Add ripple effect to show reply button
xz-dev Sep 26, 2022
8dda380
Add toolbar to comment reply dialog
xz-dev Sep 26, 2022
3f64dff
Clean the code smells which sonarcloud show
xz-dev Sep 27, 2022
6eef715
Fix comment reply dialog toolbar text color
xz-dev Sep 27, 2022
3ffcaf8
Improve "Show replies" button appearance
krlvm Sep 27, 2022
77bdf6b
A couple of visual improvements for comment replies (#3)
tsiflimagas Sep 28, 2022
f707347
FragmentContainerView replace BottomSheetDialogFragment
xz-dev Oct 28, 2022
be3a242
Fix crashes when rotate the screen
xz-dev Oct 29, 2022
f29c941
Add reply count
xz-dev Oct 29, 2022
89a1ab5
Capitalizing the first word of reply
xz-dev Nov 1, 2022
b5c0823
Update NewPipe Extractor to support handles
xz-dev Nov 5, 2022
298366a
Change comment reply toolbar color
xz-dev Nov 7, 2022
49600d4
Remove NullPointerException from clone throws list
xz-dev Nov 26, 2022
d2059db
Lower case reply
xz-dev Nov 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:6a858368c86bc9a55abee586eb6c733e86c26b97'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:eb07d70a2ce03bee3cc74fc33b2e4173e1c21436'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'

/** Checkstyle **/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;

import com.google.android.exoplayer2.PlaybackException;
Expand Down Expand Up @@ -82,7 +83,7 @@
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.EmptyFragment;
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
import org.schabi.newpipe.fragments.list.comments.CommentsFragmentContainer;
import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment;
import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
Expand Down Expand Up @@ -162,8 +163,12 @@ public final class VideoDetailFragment
private boolean showRelatedItems;
private boolean showDescription;
private String selectedTabTag;
@AttrRes @NonNull final List<Integer> tabIcons = new ArrayList<>();
@StringRes @NonNull final List<Integer> tabContentDescriptions = new ArrayList<>();
@AttrRes
@NonNull
final List<Integer> tabIcons = new ArrayList<>();
@StringRes
@NonNull
final List<Integer> tabContentDescriptions = new ArrayList<>();
private boolean tabSettingsChanged = false;
private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates

Expand Down Expand Up @@ -717,8 +722,8 @@ private View.OnTouchListener getOnControlsTouchListener() {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
animate(binding.touchAppendDetail, true, 250, AnimationType.ALPHA,
0, () ->
animate(binding.touchAppendDetail, false, 1500,
AnimationType.ALPHA, 1000));
animate(binding.touchAppendDetail, false, 1500,
AnimationType.ALPHA, 1000));
}
return false;
};
Expand Down Expand Up @@ -768,6 +773,10 @@ public boolean onBackPressed() {
Log.d(TAG, "onBackPressed() called");
}

if (callCommentFragmentOnBack()) {
return true;
}

// If we are in fullscreen mode just exit from it via first back press
if (isFullscreen()) {
if (!DeviceUtils.isTablet(activity)) {
Expand Down Expand Up @@ -800,6 +809,18 @@ public boolean onBackPressed() {
return true;
}

private boolean callCommentFragmentOnBack() {
final String currentPage = pageAdapter.getItemTitle(binding.viewPager.getCurrentItem());
if (COMMENTS_TAB_TAG.equals(currentPage)) {
final Fragment fragment = getFM()
.findFragmentById(R.id.fragment_container_view);
if (fragment instanceof BackPressable) {
return ((BackPressable) fragment).onBackPressed();
}
}
return false;
}

private void setupFromHistoryItem(final StackItem item) {
setAutoPlay(false);
hideMainPlayerOnLoadingNewStream();
Expand Down Expand Up @@ -960,7 +981,7 @@ private void initTabs() {

if (shouldShowComments()) {
pageAdapter.addFragment(
CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG);
CommentsFragmentContainer.getInstance(serviceId, url, title), COMMENTS_TAB_TAG);
tabIcons.add(R.drawable.ic_comment);
tabContentDescriptions.add(R.string.comments_tab_description);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ protected void onItemSelected(final InfoItem selectedItem) {
}
}

protected void onItemCallback(final InfoItem selectedItem) throws Exception {
if (DEBUG) {
Log.d(TAG, "onItemCallback() called with: selectedItem = [" + selectedItem + "]");
}
}

@Override
protected void initListeners() {
super.initListeners();
Expand Down Expand Up @@ -283,6 +289,14 @@ public void held(final StreamInfoItem selectedItem) {

infoListAdapter.setOnCommentsSelectedListener(this::onItemSelected);

infoListAdapter.setOnCommentsReplyListener(selectedItem -> {
try {
onItemCallback(selectedItem);
} catch (final Exception e) {
ErrorUtil.showUiErrorSnackbar(this, "Opening comment reply fragment", e);
}
});

// Ensure that there is always a scroll listener (e.g. when rotating the device)
useNormalItemListScrollListener();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.schabi.newpipe.fragments.list.comments;

import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.util.Constants;

import java.io.IOException;

import icepick.State;

public class CommentReplyFragment extends BaseFragment implements BackPressable {

@State
protected int serviceId = Constants.NO_SERVICE_ID;
@State
protected String name;
@State
protected String url;
@State
protected CommentsInfoItem comment;
@State
protected Page replies;

public static CommentReplyFragment getInstance(
final int serviceId, final String url,
final String name,
final CommentsInfoItem comment,
final Page replies
) throws IOException, ClassNotFoundException {
final CommentReplyFragment instance = new CommentReplyFragment();
instance.setInitialData(serviceId, url, name, comment, replies);
return instance;
}

public static CommentsFragmentContainer newInstance(final int serviceId, final String url,
final String name) {
final CommentsFragmentContainer fragment = new CommentsFragmentContainer();
fragment.serviceId = serviceId;
fragment.url = url;
fragment.name = name;
return new CommentsFragmentContainer();
}

@Nullable
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_comments_reply, container,
false);
final ImageButton backButton = view.findViewById(R.id.backButton);
backButton.setOnClickListener(v -> closeSelf());
final CommentsFragment commentsFragment = CommentsFragment.getInstance(
serviceId, url, name, comment
);
final CommentsFragment commentsReplyFragment = CommentsFragment.getInstance(
serviceId, url, name, replies
);
getChildFragmentManager().beginTransaction()
.add(R.id.commentFragment, commentsFragment).commit();
getChildFragmentManager().beginTransaction()
.add(R.id.commentReplyFragment, commentsReplyFragment).commit();
return view;
}

protected void setInitialData(final int sid, final String u, final String title,
final CommentsInfoItem preComment,
final Page repliesPage
) throws IOException, ClassNotFoundException {
this.serviceId = sid;
this.url = u;
this.name = !TextUtils.isEmpty(title) ? title : "";
// clone comment object to avoid replies actually set null
this.comment = CommentUtils.clone(preComment);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here

comment.setReplies(null);
this.replies = repliesPage;
}

@Override
public boolean onBackPressed() {
closeSelf();
return true;
}

private void closeSelf() {
getFM().beginTransaction().remove(this).commit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.schabi.newpipe.fragments.list.comments;

import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.util.SerializedUtils;

import java.io.IOException;

final class CommentUtils {
private CommentUtils() {
}

public static CommentsInfo clone(
final CommentsInfo item
) throws IOException, SecurityException, NullPointerException, ClassNotFoundException {
return SerializedUtils.clone(item, CommentsInfo.class);
}

public static CommentsInfoItem clone(
final CommentsInfoItem item
) throws IOException, SecurityException, NullPointerException, ClassNotFoundException {
return SerializedUtils.clone(item, CommentsInfoItem.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,68 @@

import org.schabi.newpipe.R;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.ktx.ViewUtils;
import org.schabi.newpipe.util.ExtractorHelper;

import java.util.List;

import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;

public class CommentsFragment extends BaseListInfoFragment<CommentsInfoItem, CommentsInfo> {
private final CompositeDisposable disposables = new CompositeDisposable();

private Page replies;
private CommentsInfoItem preComment;

private TextView emptyStateDesc;

public static CommentsFragment getInstance(final int serviceId, final String url,
final String name) {
final CommentsFragment instance = new CommentsFragment();
instance.setInitialData(serviceId, url, name);
instance.setInitialData(serviceId, url, name, null, null);
return instance;
}

public static CommentsFragment getInstance(final int serviceId, final String url,
final String name,
final CommentsInfoItem preComment) {
final CommentsFragment instance = new CommentsFragment();
instance.setInitialData(serviceId, url, name, null, preComment);
return instance;
}

public static CommentsFragment getInstance(final int serviceId, final String url,
final String name,
final Page replyPage) {
final CommentsFragment instance = new CommentsFragment();
instance.setInitialData(serviceId, url, name, replyPage, null);
return instance;
}

@Override
protected void onItemCallback(final InfoItem selectedItem) throws Exception {
super.onItemCallback(selectedItem);
CommentsFragmentContainer.setFragment(getFM(), (CommentsInfoItem) selectedItem);
}

public CommentsFragment() {
super(UserAction.REQUESTED_COMMENTS);
}

protected void setInitialData(final int sid, final String u, final String title,
final Page repliesPage, final CommentsInfoItem comment) {
this.replies = repliesPage;
this.preComment = comment;
super.setInitialData(sid, u, title);
}

@Override
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
Expand Down Expand Up @@ -74,7 +110,25 @@ protected Single<ListExtractor.InfoItemsPage<CommentsInfoItem>> loadMoreItemsLog

@Override
protected Single<CommentsInfo> loadResult(final boolean forceLoad) {
return ExtractorHelper.getCommentsInfo(serviceId, url, forceLoad);
if (replies == null) {
if (preComment == null) {
return ExtractorHelper.getCommentsInfo(serviceId, url, forceLoad);
} else {
return Single.fromCallable(() -> {
// get a info template
var info = ExtractorHelper.getCommentsInfo(
serviceId, url, forceLoad).blockingGet();
// clone comment object to avoid relatedItems and nextPage actually set null
info = CommentUtils.clone(info);
Comment on lines +121 to +122
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmmh, I don't like this that much. Can you explain further why you need to clone the object is a way that is so low-level?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you check CommentReplyFragment, you will know.
Because I need create a fake CommentsInfo for the CommentFragment, then I can simply put two CommentFragment together.

// push preComment
info.setRelatedItems(List.of(preComment));
info.setNextPage(null);
return info;
});
}
} else {
return ExtractorHelper.getCommentsReplyInfo(serviceId, url, forceLoad, replies);
}
}

/*//////////////////////////////////////////////////////////////////////////
Expand All @@ -99,11 +153,13 @@ public void handleResult(@NonNull final CommentsInfo result) {
//////////////////////////////////////////////////////////////////////////*/

@Override
public void setTitle(final String title) { }
public void setTitle(final String title) {
}

@Override
public void onCreateOptionsMenu(@NonNull final Menu menu,
@NonNull final MenuInflater inflater) { }
@NonNull final MenuInflater inflater) {
}

@Override
protected boolean isGridLayout() {
Expand Down
Loading