package org.wordpress.android.ui.comments; import android.app.Dialog; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.fluxc.Dispatcher; import org.wordpress.android.fluxc.generated.CommentActionBuilder; import org.wordpress.android.fluxc.model.CommentModel; import org.wordpress.android.fluxc.model.CommentStatus; import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.store.CommentStore; import org.wordpress.android.fluxc.store.CommentStore.RemoteCommentPayload; import org.wordpress.android.models.CommentList; import org.wordpress.android.models.Note; import org.wordpress.android.ui.ActivityId; import org.wordpress.android.ui.comments.CommentsListFragment.OnCommentSelectedListener; import org.wordpress.android.ui.notifications.NotificationFragment; import org.wordpress.android.ui.prefs.AppPrefs; import org.wordpress.android.ui.reader.ReaderPostDetailFragment; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.SmartToast; import org.wordpress.android.util.ToastUtils; import javax.inject.Inject; public class CommentsActivity extends AppCompatActivity implements OnCommentSelectedListener, NotificationFragment.OnPostClickListener, CommentActions.OnCommentActionListener, CommentActions.OnCommentChangeListener { private static final String KEY_SELECTED_COMMENT = "selected_comment_id"; static final String KEY_AUTO_REFRESHED = "has_auto_refreshed"; static final String KEY_EMPTY_VIEW_MESSAGE = "empty_view_message"; private static final String SAVED_COMMENTS_STATUS_TYPE = "saved_comments_status_type"; private final CommentList mTrashedComments = new CommentList(); private CommentStatus mCurrentCommentStatusType = CommentStatus.ALL; private SiteModel mSite; private CommentModel mComment; @Inject Dispatcher mDispatcher; @Inject CommentStore mCommentStore; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((WordPress) getApplication()).component().inject(this); setContentView(R.layout.comment_activity); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setElevation(0); actionBar.setTitle(R.string.comments); actionBar.setDisplayShowTitleEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); } if (savedInstanceState == null) { mSite = (SiteModel) getIntent().getSerializableExtra(WordPress.SITE); } else { mSite = (SiteModel) savedInstanceState.getSerializable(WordPress.SITE); } if (mSite == null) { ToastUtils.showToast(this, R.string.blog_not_found, ToastUtils.Duration.SHORT); finish(); return; } if (getIntent() != null && getIntent().hasExtra(SAVED_COMMENTS_STATUS_TYPE)) { mCurrentCommentStatusType = (CommentStatus) getIntent().getSerializableExtra(SAVED_COMMENTS_STATUS_TYPE); } else { // Read the value from app preferences here. Default to 0 - All mCurrentCommentStatusType = AppPrefs.getCommentsStatusFilter().toCommentStatus(); } if (savedInstanceState == null) { CommentsListFragment commentsListFragment = new CommentsListFragment(); // initialize comment status filter first time commentsListFragment.setCommentStatusFilter(mCurrentCommentStatusType); getFragmentManager().beginTransaction() .add(R.id.layout_fragment_container, commentsListFragment, getString(R.string .fragment_tag_comment_list)) .commitAllowingStateLoss(); } else { getIntent().putExtra(KEY_AUTO_REFRESHED, savedInstanceState.getBoolean(KEY_AUTO_REFRESHED)); getIntent().putExtra(KEY_EMPTY_VIEW_MESSAGE, savedInstanceState.getString(KEY_EMPTY_VIEW_MESSAGE)); mComment = (CommentModel) savedInstanceState.getSerializable(KEY_SELECTED_COMMENT); } if (savedInstanceState == null) { SmartToast.show(this, SmartToast.SmartToastType.COMMENTS_LONG_PRESS); } } @Override public void onStart() { super.onStart(); } @Override public void onStop() { super.onStop(); } @Override public void onResume() { super.onResume(); ActivityId.trackLastActivity(ActivityId.COMMENTS); } @Override public void onBackPressed() { if (getFragmentManager().getBackStackEntryCount() > 0) { getFragmentManager().popBackStack(); } else { super.onBackPressed(); } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); AppLog.d(AppLog.T.COMMENTS, "comment activity new intent"); } private CommentDetailFragment getDetailFragment() { Fragment fragment = getFragmentManager().findFragmentByTag(getString( R.string.fragment_tag_comment_detail)); if (fragment == null) { return null; } return (CommentDetailFragment) fragment; } private boolean hasDetailFragment() { return (getDetailFragment() != null); } private CommentsListFragment getListFragment() { Fragment fragment = getFragmentManager().findFragmentByTag(getString( R.string.fragment_tag_comment_list)); if (fragment == null) { return null; } return (CommentsListFragment) fragment; } private boolean hasListFragment() { return (getListFragment() != null); } private void showReaderFragment(long remoteBlogId, long postId) { FragmentManager fm = getFragmentManager(); fm.executePendingTransactions(); Fragment fragment = ReaderPostDetailFragment.newInstance(remoteBlogId, postId); FragmentTransaction ft = fm.beginTransaction(); String tagForFragment = getString(R.string.fragment_tag_reader_post_detail); ft.add(R.id.layout_fragment_container, fragment, tagForFragment) .addToBackStack(tagForFragment) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); if (hasDetailFragment()) ft.hide(getDetailFragment()); ft.commit(); } /* * called from comment list when user taps a comment */ @Override public void onCommentSelected(long commentId) { FragmentManager fm = getFragmentManager(); if (fm == null) { return; } mComment = mCommentStore.getCommentBySiteAndRemoteId(mSite, commentId); fm.executePendingTransactions(); CommentsListFragment listFragment = getListFragment(); FragmentTransaction ft = fm.beginTransaction(); String tagForFragment = getString(R.string.fragment_tag_comment_detail); CommentDetailFragment detailFragment = CommentDetailFragment.newInstance(mSite, mComment); ft.add(R.id.layout_fragment_container, detailFragment, tagForFragment).addToBackStack(tagForFragment) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); if (listFragment != null) { ft.hide(listFragment); } ft.commitAllowingStateLoss(); } /* * called from comment detail when user taps a link to a post - show the post in a * reader detail fragment */ @Override public void onPostClicked(Note note, long remoteBlogId, int postId) { showReaderFragment(remoteBlogId, postId); } /* * reload the comment list from existing data */ private void reloadCommentList() { CommentsListFragment listFragment = getListFragment(); if (listFragment != null) listFragment.loadComments(); } /* * tell the comment list to get recent comments from server */ private void updateCommentList() { CommentsListFragment listFragment = getListFragment(); if (listFragment != null) { //listFragment.setRefreshing(true); listFragment.setCommentStatusFilter(mCurrentCommentStatusType); listFragment.updateComments(false); } } @Override public void onSaveInstanceState(@NonNull Bundle outState) { outState.putSerializable(WordPress.SITE, mSite); // retain the id of the highlighted and selected comments if (mComment != null && hasDetailFragment()) { outState.putSerializable(KEY_SELECTED_COMMENT, mComment); } if (hasListFragment()) { outState.putBoolean(KEY_AUTO_REFRESHED, getListFragment().mHasAutoRefreshedComments); outState.putString(KEY_EMPTY_VIEW_MESSAGE, getListFragment().getEmptyViewMessage()); } super.onSaveInstanceState(outState); } @Override protected Dialog onCreateDialog(int id) { Dialog dialog = CommentDialogs.createCommentDialog(this, id); if (dialog != null) return dialog; return super.onCreateDialog(id); } @Override public void onModerateComment(final SiteModel site, final CommentModel comment, final CommentStatus newStatus) { FragmentManager fm = getFragmentManager(); if (fm.getBackStackEntryCount() > 0) { fm.popBackStack(); } if (newStatus == CommentStatus.APPROVED || newStatus == CommentStatus.UNAPPROVED) { getListFragment().updateEmptyView(); comment.setStatus(newStatus.toString()); mDispatcher.dispatch(CommentActionBuilder.newUpdateCommentAction(comment)); mDispatcher.dispatch(CommentActionBuilder.newPushCommentAction(new RemoteCommentPayload(mSite, comment))); } else if (newStatus == CommentStatus.SPAM || newStatus == CommentStatus.TRASH || newStatus == CommentStatus.DELETED) { mTrashedComments.add(comment); getListFragment().removeComment(comment); String message = (newStatus == CommentStatus.TRASH ? getString(R.string.comment_trashed) : newStatus == CommentStatus.SPAM ? getString(R.string.comment_spammed) : getString(R.string.comment_deleted_permanently) ); View.OnClickListener undoListener = new View.OnClickListener() { @Override public void onClick(View v) { mTrashedComments.remove(comment); getListFragment().loadComments(); } }; Snackbar snackbar = Snackbar.make(getListFragment().getView(), message, Snackbar.LENGTH_LONG) .setAction(R.string.undo, undoListener); // do the actual moderation once the undo bar has been hidden snackbar.setCallback(new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { super.onDismissed(snackbar, event); // comment will no longer exist in moderating list if action was undone if (!mTrashedComments.contains(comment)) { return; } mTrashedComments.remove(comment); moderateComment(comment, newStatus); } }); snackbar.show(); } } private void moderateComment(CommentModel comment, CommentStatus newStatus) { if (newStatus == CommentStatus.DELETED) { // For deletion, we need to dispatch a specific action. mDispatcher.dispatch(CommentActionBuilder.newDeleteCommentAction(new RemoteCommentPayload(mSite, comment))); } else { // Actual moderation (push the modified comment). comment.setStatus(newStatus.toString()); mDispatcher.dispatch(CommentActionBuilder.newPushCommentAction(new RemoteCommentPayload(mSite, comment))); } } @Override public void onCommentChanged(CommentActions.ChangeType changeType) { switch (changeType) { case EDITED: reloadCommentList(); break; case REPLIED: updateCommentList(); break; } } @Override public boolean onOptionsItemSelected(final MenuItem item) { if (item.getItemId() == android.R.id.home) { onBackPressed(); return true; } return super.onOptionsItemSelected(item); } }