diff --git a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java index f3fe37d426d6..e61ed12fab51 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java @@ -58,6 +58,7 @@ import org.wordpress.android.ui.mlp.ModalLayoutPickerFragment; import org.wordpress.android.ui.mysite.MySiteFragment; import org.wordpress.android.ui.notifications.DismissNotificationReceiver; +import org.wordpress.android.ui.notifications.MilestoneDetailFragment; import org.wordpress.android.ui.notifications.NotificationsDetailActivity; import org.wordpress.android.ui.notifications.NotificationsDetailListFragment; import org.wordpress.android.ui.notifications.NotificationsListFragmentPage; @@ -303,6 +304,8 @@ public interface AppComponent { void inject(NotificationsDetailListFragment object); + void inject(MilestoneDetailFragment object); + void inject(ReaderSubsActivity object); void inject(ReaderUpdateLogic object); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/MilestoneDetailFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/MilestoneDetailFragment.kt new file mode 100644 index 000000000000..3a95eb6d53a2 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/MilestoneDetailFragment.kt @@ -0,0 +1,228 @@ +package org.wordpress.android.ui.notifications + +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.fragment.app.ListFragment +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.json.JSONArray +import org.json.JSONException +import org.wordpress.android.R +import org.wordpress.android.WordPress +import org.wordpress.android.datasets.NotificationsTable +import org.wordpress.android.models.Note +import org.wordpress.android.modules.IO_THREAD +import org.wordpress.android.modules.UI_THREAD +import org.wordpress.android.ui.ScrollableViewInitializedListener +import org.wordpress.android.ui.ViewPagerFragment.Companion.restoreOriginalViewId +import org.wordpress.android.ui.ViewPagerFragment.Companion.setUniqueIdToView +import org.wordpress.android.ui.notifications.adapters.NoteBlockAdapter +import org.wordpress.android.ui.notifications.blocks.MilestoneNoteBlock +import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper +import org.wordpress.android.util.AppLog +import org.wordpress.android.util.AppLog.T.NOTIFS +import org.wordpress.android.util.ToastUtils +import org.wordpress.android.util.image.ImageManager +import javax.inject.Inject +import javax.inject.Named + +class MilestoneDetailFragment : ListFragment(), NotificationFragment { + private var restoredListPosition = 0 + private var notification: Note? = null + private var rootLayout: LinearLayout? = null + private var restoredNoteId: String? = null + private var noteBlockAdapter: NoteBlockAdapter? = null + + @Inject + lateinit var imageManager: ImageManager + + @Inject + lateinit var notificationsUtilsWrapper: NotificationsUtilsWrapper + + @Inject + @Named(IO_THREAD) + lateinit var ioDispatcher: CoroutineDispatcher + + @Inject + @Named(UI_THREAD) + lateinit var mainDispatcher: CoroutineDispatcher + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + (requireActivity().application as WordPress).component().inject(this) + if (savedInstanceState != null && savedInstanceState.containsKey(KEY_NOTE_ID)) { + restoredNoteId = savedInstanceState.getString(KEY_NOTE_ID) + restoredListPosition = savedInstanceState.getInt(KEY_LIST_POSITION, 0) + } else { + arguments?.let { + setNote(it.getString(KEY_NOTE_ID)) { + reloadNoteBlocks() + } + } + } + } + + override fun onSaveInstanceState(outState: Bundle) { + notification?.let { + outState.putString(KEY_NOTE_ID, it.id) + outState.putInt(KEY_LIST_POSITION, listView.firstVisiblePosition) + } ?: run { + // This is done so the fragments pre-loaded by the view pager can store the already rescued restoredNoteId + if (!TextUtils.isEmpty(restoredNoteId)) { + outState.putString(KEY_NOTE_ID, restoredNoteId) + } + } + + super.onSaveInstanceState(outState) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.notifications_fragment_detail_list, container, false) + rootLayout = view.findViewById(R.id.notifications_list_root) + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val listView = listView + listView.divider = null + listView.dividerHeight = 0 + listView.setHeaderDividersEnabled(false) + } + + override fun onResume() { + super.onResume() + setUniqueIdToView(listView) + if (activity is ScrollableViewInitializedListener) { + (activity as ScrollableViewInitializedListener).onScrollableViewInitialized(listView.id) + } + + // Set the note if we retrieved the noteId from savedInstanceState + if (!TextUtils.isEmpty(restoredNoteId)) { + setNote(restoredNoteId) { + reloadNoteBlocks() + restoredNoteId = null + } + } + } + + override fun onPause() { + restoreOriginalViewId(listView) + super.onPause() + } + + private fun setNote(noteId: String?, onNoteSet: (() -> Unit)? = null) { + if (noteId == null) { + showErrorToastAndFinish() + return + } + lifecycleScope.launch(ioDispatcher) { + val note: Note? = NotificationsTable.getNoteById(noteId) + withContext(mainDispatcher) { + if (note == null) { + showErrorToastAndFinish() + } else { + notification = note + onNoteSet?.invoke() + } + } + } + } + + private fun showErrorToastAndFinish() { + AppLog.e(NOTIFS, "Note could not be found.") + activity?.let { + ToastUtils.showToast(activity, R.string.error_notification_open) + it.finish() + } + } + + private fun reloadNoteBlocks() { + lifecycleScope.launch(ioDispatcher) { + notification?.let { note -> + val noteBlocks = noteBlocksLoader.loadNoteBlocks(note) + withContext(mainDispatcher) { + noteBlocksLoader.handleNoteBlocks(noteBlocks) + } + } + } + } + + private val mOnNoteBlockTextClickListener = NoteBlockTextClickListener(this, notification) + + // Loop through the 'body' items in this note, and create blocks for each. + private val noteBlocksLoader = object { + private fun addNotesBlock(noteList: MutableList, bodyArray: JSONArray) { + var i = 0 + while (i < bodyArray.length()) { + try { + val noteObject = notificationsUtilsWrapper + .mapJsonToFormattableContent(bodyArray.getJSONObject(i)) + + val noteBlock = MilestoneNoteBlock( + noteObject, imageManager, notificationsUtilsWrapper, + mOnNoteBlockTextClickListener + ) + preloadImage(noteBlock) + noteList.add(noteBlock) + } catch (e: JSONException) { + AppLog.e(NOTIFS, "Error parsing milestone note data.") + } + i++ + } + } + + private fun preloadImage(noteBlock: MilestoneNoteBlock) { + if (noteBlock.hasImageMediaItem()) { + noteBlock.noteMediaItem?.url?.let { + imageManager.preload(requireContext(), it) + } + } + } + + fun loadNoteBlocks(note: Note): List { + val bodyArray = note.body + val noteList: MutableList = ArrayList() + + if (bodyArray.length() > 0) { + addNotesBlock(noteList, bodyArray) + } + return noteList + } + + fun handleNoteBlocks(noteList: List?) { + if (!isAdded || noteList == null) { + return + } + if (noteBlockAdapter == null) { + noteBlockAdapter = NoteBlockAdapter(requireContext(), noteList) + listAdapter = noteBlockAdapter + } else { + noteBlockAdapter?.setNoteList(noteList) + } + if (restoredListPosition > 0) { + listView.setSelectionFromTop(restoredListPosition, 0) + restoredListPosition = 0 + } + } + } + + companion object { + private const val KEY_NOTE_ID = "noteId" + private const val KEY_LIST_POSITION = "listPosition" + + @JvmStatic + fun newInstance(noteId: String?): MilestoneDetailFragment { + val fragment = MilestoneDetailFragment() + val bundle = Bundle().apply { putString(KEY_NOTE_ID, noteId) } + fragment.arguments = bundle + return fragment + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NoteBlockTextClickListener.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NoteBlockTextClickListener.kt new file mode 100644 index 000000000000..235905e1d52a --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NoteBlockTextClickListener.kt @@ -0,0 +1,139 @@ +@file:Suppress("DEPRECATION") + +package org.wordpress.android.ui.notifications + +import android.text.TextUtils +import android.view.View +import androidx.fragment.app.Fragment +import org.wordpress.android.datasets.ReaderPostTable +import org.wordpress.android.fluxc.tools.FormattableRangeType +import org.wordpress.android.models.Note +import org.wordpress.android.ui.comments.CommentDetailFragment +import org.wordpress.android.ui.comments.unified.CommentActionPopupHandler +import org.wordpress.android.ui.notifications.blocks.NoteBlock +import org.wordpress.android.ui.notifications.blocks.NoteBlockClickableSpan +import org.wordpress.android.ui.reader.ReaderActivityLauncher +import org.wordpress.android.ui.reader.comments.ThreadedCommentsActionSource +import org.wordpress.android.ui.reader.utils.ReaderUtils + +class NoteBlockTextClickListener( + val fragment: Fragment, + val notification: Note?, + private val onActionClickListener: CommentDetailFragment.OnActionClickListener? = null +) : NoteBlock.OnNoteBlockTextClickListener { + override fun onNoteBlockTextClicked(clickedSpan: NoteBlockClickableSpan?) { + if (!fragment.isAdded || fragment.activity !is NotificationsDetailActivity) { + return + } + clickedSpan?.let { handleNoteBlockSpanClick(fragment.activity as NotificationsDetailActivity, it) } + } + + override fun showDetailForNoteIds() { + if (!fragment.isAdded || notification == null || fragment.activity !is NotificationsDetailActivity) { + return + } + val detailActivity = fragment.activity as NotificationsDetailActivity + + requireNotNull(notification).let { note -> + if (note.isCommentReplyType || !note.isCommentType && note.commentId > 0) { + val commentId = if (note.isCommentReplyType) note.parentCommentId else note.commentId + + // show comments list if it exists in the reader + if (ReaderUtils.postAndCommentExists(note.siteId.toLong(), note.postId.toLong(), commentId)) { + detailActivity.showReaderCommentsList(note.siteId.toLong(), note.postId.toLong(), commentId) + } else { + detailActivity.showWebViewActivityForUrl(note.url) + } + } else if (note.isFollowType) { + detailActivity.showBlogPreviewActivity(note.siteId.toLong(), note.isFollowType) + } else { + // otherwise, load the post in the Reader + detailActivity.showPostActivity(note.siteId.toLong(), note.postId.toLong()) + } + } + } + + override fun showReaderPostComments() { + if (!fragment.isAdded || notification == null || notification.commentId == 0L) { + return + } + + requireNotNull(notification).let { note -> + fragment.context?.let { nonNullContext -> + ReaderActivityLauncher.showReaderComments( + nonNullContext, note.siteId.toLong(), note.postId.toLong(), + note.commentId, + ThreadedCommentsActionSource.COMMENT_NOTIFICATION.sourceDescription + ) + } + } + } + + override fun showSitePreview(siteId: Long, siteUrl: String?) { + if (!fragment.isAdded || notification == null || fragment.activity !is NotificationsDetailActivity) { + return + } + val detailActivity = fragment.activity as NotificationsDetailActivity + if (siteId != 0L) { + detailActivity.showBlogPreviewActivity(siteId, notification.isFollowType) + } else if (!TextUtils.isEmpty(siteUrl)) { + detailActivity.showWebViewActivityForUrl(siteUrl) + } + } + + override fun showActionPopup(view: View) { + CommentActionPopupHandler.show(view, onActionClickListener) + } + + fun handleNoteBlockSpanClick( + activity: NotificationsDetailActivity, + clickedSpan: NoteBlockClickableSpan + ) { + when (clickedSpan.rangeType) { + FormattableRangeType.SITE -> + // Show blog preview + activity.showBlogPreviewActivity(clickedSpan.id, notification?.isFollowType) + + FormattableRangeType.USER -> + // Show blog preview + activity.showBlogPreviewActivity(clickedSpan.siteId, notification?.isFollowType) + + FormattableRangeType.POST -> + // Show post detail + activity.showPostActivity(clickedSpan.siteId, clickedSpan.id) + + FormattableRangeType.COMMENT -> + // Load the comment in the reader list if it exists, otherwise show a webview + if (ReaderUtils.postAndCommentExists( + clickedSpan.siteId, clickedSpan.postId, + clickedSpan.id + ) + ) { + activity.showReaderCommentsList( + clickedSpan.siteId, clickedSpan.postId, + clickedSpan.id + ) + } else { + activity.showWebViewActivityForUrl(clickedSpan.url) + } + + FormattableRangeType.SCAN -> activity.showScanActivityForSite(clickedSpan.siteId) + FormattableRangeType.STAT, FormattableRangeType.FOLLOW -> + // We can open native stats if the site is a wpcom or Jetpack sites + activity.showStatsActivityForSite(clickedSpan.siteId, clickedSpan.rangeType) + + FormattableRangeType.LIKE -> if (ReaderPostTable.postExists(clickedSpan.siteId, clickedSpan.id)) { + activity.showReaderPostLikeUsers(clickedSpan.siteId, clickedSpan.id) + } else { + activity.showPostActivity(clickedSpan.siteId, clickedSpan.id) + } + + FormattableRangeType.REWIND_DOWNLOAD_READY -> activity.showBackupForSite(clickedSpan.siteId) + else -> + // We don't know what type of id this is, let's see if it has a URL and push a webview + if (!TextUtils.isEmpty(clickedSpan.url)) { + activity.showWebViewActivityForUrl(clickedSpan.url) + } + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailActivity.java index 6b8f640ed6ac..8cc91abeb3e2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailActivity.java @@ -431,6 +431,8 @@ private Fragment createDetailFragmentForNote(@NonNull Note note) { note.getSiteId(), note.getPostId() ); + } else if (NoteExtensions.isAchievement(note)) { + fragment = MilestoneDetailFragment.newInstance(note.getId()); } else { if (mLikesEnhancementsFeatureConfig.isEnabled() && note.isLikeType()) { fragment = EngagedPeopleListFragment.newInstance( diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailListFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailListFragment.kt index 687157dcaeee..c261b9bd9c7e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailListFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsDetailListFragment.kt @@ -15,7 +15,6 @@ import android.widget.LinearLayout import android.widget.ListView import androidx.fragment.app.ListFragment import androidx.lifecycle.lifecycleScope -import com.airbnb.lottie.LottieAnimationView import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -28,15 +27,6 @@ import org.wordpress.android.datasets.ReaderCommentTable import org.wordpress.android.datasets.ReaderPostTable import org.wordpress.android.fluxc.model.CommentStatus import org.wordpress.android.fluxc.tools.FormattableContent -import org.wordpress.android.fluxc.tools.FormattableRangeType.COMMENT -import org.wordpress.android.fluxc.tools.FormattableRangeType.FOLLOW -import org.wordpress.android.fluxc.tools.FormattableRangeType.LIKE -import org.wordpress.android.fluxc.tools.FormattableRangeType.POST -import org.wordpress.android.fluxc.tools.FormattableRangeType.REWIND_DOWNLOAD_READY -import org.wordpress.android.fluxc.tools.FormattableRangeType.SCAN -import org.wordpress.android.fluxc.tools.FormattableRangeType.SITE -import org.wordpress.android.fluxc.tools.FormattableRangeType.STAT -import org.wordpress.android.fluxc.tools.FormattableRangeType.USER import org.wordpress.android.models.Note import org.wordpress.android.modules.IO_THREAD import org.wordpress.android.modules.UI_THREAD @@ -44,7 +34,6 @@ import org.wordpress.android.ui.ScrollableViewInitializedListener import org.wordpress.android.ui.ViewPagerFragment.Companion.restoreOriginalViewId import org.wordpress.android.ui.ViewPagerFragment.Companion.setUniqueIdToView import org.wordpress.android.ui.comments.CommentDetailFragment -import org.wordpress.android.ui.comments.unified.CommentActionPopupHandler import org.wordpress.android.ui.engagement.ListScenarioUtils import org.wordpress.android.ui.notifications.adapters.NoteBlockAdapter import org.wordpress.android.ui.notifications.blocks.BlockType @@ -55,15 +44,11 @@ import org.wordpress.android.ui.notifications.blocks.GeneratedNoteBlock import org.wordpress.android.ui.notifications.blocks.HeaderNoteBlock import org.wordpress.android.ui.notifications.blocks.NoteBlock import org.wordpress.android.ui.notifications.blocks.NoteBlock.OnNoteBlockTextClickListener -import org.wordpress.android.ui.notifications.blocks.NoteBlockClickableSpan import org.wordpress.android.ui.notifications.blocks.UserNoteBlock import org.wordpress.android.ui.notifications.blocks.UserNoteBlock.OnGravatarClickedListener import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper -import org.wordpress.android.ui.reader.ReaderActivityLauncher import org.wordpress.android.ui.reader.actions.ReaderPostActions -import org.wordpress.android.ui.reader.comments.ThreadedCommentsActionSource.COMMENT_NOTIFICATION import org.wordpress.android.ui.reader.services.comment.ReaderCommentService -import org.wordpress.android.ui.reader.utils.ReaderUtils import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T.NOTIFS import org.wordpress.android.util.ToastUtils @@ -110,18 +95,21 @@ class NotificationsDetailListFragment : ListFragment(), NotificationFragment { // See WordPress.deferredInit() restoredNoteId = savedInstanceState.getString(KEY_NOTE_ID) restoredListPosition = savedInstanceState.getInt(KEY_LIST_POSITION, 0) + } else { + arguments?.let { + setNote(it.getString(KEY_NOTE_ID)) + } } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.notifications_fragment_detail_list, container, false) - rootLayout = view.findViewById(R.id.notifications_list_root) as LinearLayout + rootLayout = view.findViewById(R.id.notifications_list_root)!! return view } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") - override fun onActivityCreated(bundle: Bundle?) { - super.onActivityCreated(bundle) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) val listView = listView listView.divider = null listView.dividerHeight = 0 @@ -148,14 +136,6 @@ class NotificationsDetailListFragment : ListFragment(), NotificationFragment { if (notification == null) { showErrorToastAndFinish() } - - val animation = view?.findViewById(R.id.confetti) - if (notification?.isViewMilestoneType == true) { - animation?.visibility = View.VISIBLE - animation?.playAnimation() - } else { - animation?.visibility = View.GONE - } } override fun onPause() { @@ -215,117 +195,7 @@ class NotificationsDetailListFragment : ListFragment(), NotificationFragment { this.footerView = footerView } - private val mOnNoteBlockTextClickListener: OnNoteBlockTextClickListener = object : OnNoteBlockTextClickListener { - override fun onNoteBlockTextClicked(clickedSpan: NoteBlockClickableSpan?) { - if (!isAdded || activity !is NotificationsDetailActivity) { - return - } - clickedSpan?.let { handleNoteBlockSpanClick(activity as NotificationsDetailActivity, it) } - } - - override fun showDetailForNoteIds() { - if (!isAdded || notification == null || activity !is NotificationsDetailActivity) { - return - } - val detailActivity = activity as NotificationsDetailActivity - - requireNotNull(notification).let { note -> - if (note.isCommentReplyType || !note.isCommentType && note.commentId > 0) { - val commentId = if (note.isCommentReplyType) note.parentCommentId else note.commentId - - // show comments list if it exists in the reader - if (ReaderUtils.postAndCommentExists(note.siteId.toLong(), note.postId.toLong(), commentId)) { - detailActivity.showReaderCommentsList(note.siteId.toLong(), note.postId.toLong(), commentId) - } else { - detailActivity.showWebViewActivityForUrl(note.url) - } - } else if (note.isFollowType) { - detailActivity.showBlogPreviewActivity(note.siteId.toLong(), note.isFollowType) - } else { - // otherwise, load the post in the Reader - detailActivity.showPostActivity(note.siteId.toLong(), note.postId.toLong()) - } - } - } - - override fun showReaderPostComments() { - if (!isAdded || notification == null || notification!!.commentId == 0L) { - return - } - - requireNotNull(notification).let { note -> - context?.let { nonNullContext -> - ReaderActivityLauncher.showReaderComments( - nonNullContext, note.siteId.toLong(), note.postId.toLong(), - note.commentId, - COMMENT_NOTIFICATION.sourceDescription - ) - } - } - } - - override fun showSitePreview(siteId: Long, siteUrl: String?) { - if (!isAdded || notification == null || activity !is NotificationsDetailActivity) { - return - } - val detailActivity = activity as NotificationsDetailActivity - if (siteId != 0L) { - detailActivity.showBlogPreviewActivity(siteId, notification?.isFollowType) - } else if (!TextUtils.isEmpty(siteUrl)) { - detailActivity.showWebViewActivityForUrl(siteUrl) - } - } - - override fun showActionPopup(view: View) { - CommentActionPopupHandler.show(view, onActionClickListener) - } - - fun handleNoteBlockSpanClick( - activity: NotificationsDetailActivity, - clickedSpan: NoteBlockClickableSpan - ) { - when (clickedSpan.rangeType) { - SITE -> - // Show blog preview - activity.showBlogPreviewActivity(clickedSpan.id, notification?.isFollowType) - USER -> - // Show blog preview - activity.showBlogPreviewActivity(clickedSpan.siteId, notification?.isFollowType) - POST -> - // Show post detail - activity.showPostActivity(clickedSpan.siteId, clickedSpan.id) - COMMENT -> - // Load the comment in the reader list if it exists, otherwise show a webview - if (ReaderUtils.postAndCommentExists( - clickedSpan.siteId, clickedSpan.postId, - clickedSpan.id - ) - ) { - activity.showReaderCommentsList( - clickedSpan.siteId, clickedSpan.postId, - clickedSpan.id - ) - } else { - activity.showWebViewActivityForUrl(clickedSpan.url) - } - SCAN -> activity.showScanActivityForSite(clickedSpan.siteId) - STAT, FOLLOW -> - // We can open native stats if the site is a wpcom or Jetpack sites - activity.showStatsActivityForSite(clickedSpan.siteId, clickedSpan.rangeType) - LIKE -> if (ReaderPostTable.postExists(clickedSpan.siteId, clickedSpan.id)) { - activity.showReaderPostLikeUsers(clickedSpan.siteId, clickedSpan.id) - } else { - activity.showPostActivity(clickedSpan.siteId, clickedSpan.id) - } - REWIND_DOWNLOAD_READY -> activity.showBackupForSite(clickedSpan.siteId) - else -> - // We don't know what type of id this is, let's see if it has a URL and push a webview - if (!TextUtils.isEmpty(clickedSpan.url)) { - activity.showWebViewActivityForUrl(clickedSpan.url) - } - } - } - } + private val mOnNoteBlockTextClickListener = NoteBlockTextClickListener(this, notification, onActionClickListener) private val mOnGravatarClickedListener = object : OnGravatarClickedListener { override fun onGravatarClicked(siteId: Long, userId: Long, siteUrl: String?) { @@ -460,9 +330,6 @@ class NotificationsDetailListFragment : ListFragment(), NotificationFragment { if (mIsBadgeView) { noteBlock.setIsBadge() } - if (note.isViewMilestoneType) { - noteBlock.setIsViewMilestone() - } if (isPingback) { noteBlock.setIsPingback() } @@ -675,7 +542,8 @@ class NotificationsDetailListFragment : ListFragment(), NotificationFragment { @JvmStatic fun newInstance(noteId: String?): NotificationsDetailListFragment { val fragment = NotificationsDetailListFragment() - fragment.setNote(noteId) + val bundle = Bundle().apply { putString(KEY_NOTE_ID, noteId) } + fragment.arguments = bundle return fragment } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/MilestoneNoteBlock.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/MilestoneNoteBlock.kt new file mode 100644 index 000000000000..9fdf7b0cb66b --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/MilestoneNoteBlock.kt @@ -0,0 +1,29 @@ +package org.wordpress.android.ui.notifications.blocks + +import android.text.Spannable +import android.text.SpannableString +import org.wordpress.android.R +import org.wordpress.android.fluxc.tools.FormattableContent +import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper +import org.wordpress.android.util.image.ImageManager + +class MilestoneNoteBlock( + noteData: FormattableContent, + imageManager: ImageManager, + notificationsUtilsWrapper: NotificationsUtilsWrapper, + onNoteBlockTextClickListener: OnNoteBlockTextClickListener?, +) : NoteBlock( + noteData, + imageManager, + notificationsUtilsWrapper, + onNoteBlockTextClickListener +) { + override var mIsBadge = true + override var mIsViewMilestone = true + override val layoutResourceId: Int + get() = R.layout.note_block_milestone + + // The first item of the list which contains the badge is skipped because it is used for the legacy screen's title. + override val noteText: Spannable + get() = if(containsBadgeMediaType()) SpannableString("") else super.noteText +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/NoteBlock.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/NoteBlock.kt index 2724891f89ed..fe914b8adee3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/NoteBlock.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/blocks/NoteBlock.kt @@ -41,9 +41,9 @@ open class NoteBlock( @JvmField protected val mNotificationsUtilsWrapper: NotificationsUtilsWrapper, private val mOnNoteBlockTextClickListener: OnNoteBlockTextClickListener? ) { - private var mIsBadge = false + protected open var mIsBadge = false private var isPingBack = false - private var mIsViewMilestone = false + protected open var mIsViewMilestone = false interface OnNoteBlockTextClickListener { fun onNoteBlockTextClicked(clickedSpan: NoteBlockClickableSpan?) diff --git a/WordPress/src/main/res/layout/note_block_milestone.xml b/WordPress/src/main/res/layout/note_block_milestone.xml new file mode 100644 index 000000000000..33c55a87f335 --- /dev/null +++ b/WordPress/src/main/res/layout/note_block_milestone.xml @@ -0,0 +1,63 @@ + + + + + + + +