From f8390d0473d00e90bb58d8898d1e3b3167fec71c Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Wed, 31 Jan 2024 18:15:43 +0800 Subject: [PATCH 01/13] Fix the margins of notification --- WordPress/src/main/res/layout/notifications_list_item.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/res/layout/notifications_list_item.xml b/WordPress/src/main/res/layout/notifications_list_item.xml index 627f6161fee0..3227bb9e1925 100644 --- a/WordPress/src/main/res/layout/notifications_list_item.xml +++ b/WordPress/src/main/res/layout/notifications_list_item.xml @@ -67,8 +67,9 @@ android:id="@+id/note_subject_container" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/margin_extra_large" + android:layout_marginEnd="@dimen/margin_none" android:layout_marginStart="@dimen/margin_medium_large" + app:layout_goneMarginEnd="@dimen/margin_extra_large" app:layout_constraintEnd_toStartOf="@+id/action" app:layout_constraintStart_toEndOf="@+id/note_avatar" app:layout_constraintTop_toTopOf="parent"> @@ -99,13 +100,14 @@ android:id="@+id/note_detail" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/margin_extra_large" + android:layout_marginEnd="@dimen/margin_none" android:layout_marginStart="@dimen/margin_medium_large" android:ellipsize="end" android:importantForAccessibility="no" android:maxLines="2" android:textAppearance="@style/WordPress.TextAppearance.NotificationItemContent" android:visibility="gone" + app:layout_goneMarginEnd="@dimen/margin_extra_large" app:layout_constraintEnd_toStartOf="@id/action" app:layout_constraintStart_toEndOf="@+id/note_avatar" app:layout_constraintTop_toBottomOf="@+id/note_subject_container" From 14ad09abf57a01c4108916f4533d24e84bcde798 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 1 Feb 2024 17:40:54 +0800 Subject: [PATCH 02/13] Handle the visibility of action icon --- .../android/ui/notifications/adapters/NotesAdapter.java | 8 ++++++++ WordPress/src/main/res/layout/notifications_list_item.xml | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.java index 3ab7e7f1e6a5..926be56da0ad 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.java @@ -304,6 +304,12 @@ public void onBindViewHolder(NoteViewHolder noteViewHolder, int position) { MarginLayoutParams layoutParams = (MarginLayoutParams) noteViewHolder.mHeaderText.getLayoutParams(); layoutParams.topMargin = headerMarginTop; noteViewHolder.mHeaderText.setLayoutParams(layoutParams); + + handleInlineActions(noteViewHolder, note); + } + + private void handleInlineActions(final NoteViewHolder noteViewHolder, final Note note) { + note.getType(); } private void handleMaxLines(final TextView subject, final TextView detail) { @@ -378,6 +384,7 @@ class NoteViewHolder extends RecyclerView.ViewHolder { private final TextView mTxtDetail; private final ImageView mImgAvatar; private final View mUnreadNotificationView; + private final ImageView mAction; NoteViewHolder(View view) { super(view); @@ -388,6 +395,7 @@ class NoteViewHolder extends RecyclerView.ViewHolder { mTxtDetail = view.findViewById(R.id.note_detail); mImgAvatar = view.findViewById(R.id.note_avatar); mUnreadNotificationView = view.findViewById(R.id.notification_unread); + mAction = view.findViewById(R.id.action); mContentView.setOnClickListener(mOnClickListener); } diff --git a/WordPress/src/main/res/layout/notifications_list_item.xml b/WordPress/src/main/res/layout/notifications_list_item.xml index 3227bb9e1925..5b6ae16da66b 100644 --- a/WordPress/src/main/res/layout/notifications_list_item.xml +++ b/WordPress/src/main/res/layout/notifications_list_item.xml @@ -55,12 +55,14 @@ android:id="@+id/action" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:foreground="?attr/selectableItemBackgroundBorderless" + android:background="?attr/selectableItemBackgroundBorderless" android:padding="@dimen/notifications_item_action_padding" android:contentDescription="@null" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/note_avatar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/note_avatar" + tools:visibility="visible" tools:src="@drawable/star_empty" /> Date: Mon, 12 Feb 2024 21:15:07 +0800 Subject: [PATCH 03/13] Handle the like action --- .../ui/notifications/adapters/NotesAdapter.kt | 60 +++++++++++++++++++ .../src/main/res/drawable/star_filled.xml | 9 +++ 2 files changed, 69 insertions(+) create mode 100644 WordPress/src/main/res/drawable/star_filled.xml diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt index bc5cd431fe55..12d304f808d7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt @@ -14,27 +14,41 @@ import android.view.ViewGroup.MarginLayoutParams import android.view.ViewTreeObserver import android.widget.ImageView import android.widget.TextView +import androidx.appcompat.content.res.AppCompatResources import androidx.core.text.BidiFormatter import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.greenrobot.eventbus.EventBus import org.wordpress.android.R import org.wordpress.android.WordPress import org.wordpress.android.datasets.NotificationsTable +import org.wordpress.android.datasets.ReaderPostTable +import org.wordpress.android.fluxc.generated.CommentActionBuilder +import org.wordpress.android.fluxc.store.CommentStore.RemoteLikeCommentPayload +import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.models.Note import org.wordpress.android.models.Note.NoteTimeGroup import org.wordpress.android.models.Note.TimeStampComparator +import org.wordpress.android.models.ReaderPost import org.wordpress.android.ui.comments.CommentUtils +import org.wordpress.android.ui.comments.unified.CommentsStoreAdapter +import org.wordpress.android.ui.notifications.NotificationEvents.NoteLikeOrModerationStatusChanged import org.wordpress.android.ui.notifications.NotificationsListFragmentPage.OnNoteClickListener import org.wordpress.android.ui.notifications.adapters.NotesAdapter.NoteViewHolder import org.wordpress.android.ui.notifications.blocks.NoteBlockClickableSpan import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper +import org.wordpress.android.ui.reader.repository.usecases.PostLikeUseCase import org.wordpress.android.util.GravatarUtils import org.wordpress.android.util.RtlUtils import org.wordpress.android.util.image.ImageManager import org.wordpress.android.util.image.ImageType import javax.inject.Inject + class NotesAdapter( context: Context, dataLoadedListener: DataLoadedListener, onLoadMoreListener: OnLoadMoreListener? @@ -54,6 +68,13 @@ class NotesAdapter( @Inject var notificationsUtilsWrapper: NotificationsUtilsWrapper? = null + @Inject + lateinit var siteStore: SiteStore + @Inject + lateinit var commentStoreAdapter: CommentsStoreAdapter + @Inject + lateinit var postLikeUseCase: PostLikeUseCase + enum class FILTERS { FILTER_ALL, FILTER_COMMENT, @@ -222,6 +243,43 @@ class NotesAdapter( val layoutParams = noteViewHolder.headerText.layoutParams as MarginLayoutParams layoutParams.topMargin = headerMarginTop noteViewHolder.headerText.layoutParams = layoutParams + + // handle inline actions + when { + note.isCommentType || note.isNewPostType -> { + handlePostLikeAction(note, noteViewHolder) + } + } + } + + private fun handlePostLikeAction(note: Note, holder: NoteViewHolder) { + val post = ReaderPostTable.getBlogPost(note.siteId.toLong(), note.postId.toLong(), true) ?: return + if(post.canLikePost().not()) return + + val icon = if (post.isLikedByCurrentUser) R.drawable.star_filled else R.drawable.star_empty + GlobalScope.launch { postLikeUseCase.perform(post, !post.isLikedByCurrentUser, "notification") } + } + + private fun handleCommentLikeAction(note: Note, holder: NoteViewHolder) { + val canLike = note.canLike() + if (canLike.not()) return + + val site = siteStore.getSiteBySiteId(note.siteId.toLong())!! + val comment = commentStoreAdapter.getCommentBySiteAndRemoteId(site, note.commentId)!! + val icon = if (comment.iLike) R.drawable.star_filled else R.drawable.star_empty + holder.action.isVisible = true + holder.action.setImageDrawable(AppCompatResources.getDrawable(holder.contentView.context, icon)) + holder.action.setOnClickListener { + commentStoreAdapter.dispatch( + CommentActionBuilder.newLikeCommentAction( + RemoteLikeCommentPayload(site, comment, comment.iLike.not()) + ) + ) + comment.iLike = comment.iLike.not() + val iconId = if (comment.iLike) R.drawable.star_filled else R.drawable.star_empty + holder.action.setImageDrawable(AppCompatResources.getDrawable(holder.contentView.context, iconId)) + EventBus.getDefault().postSticky(NoteLikeOrModerationStatusChanged(note.id)) + } } private fun NoteViewHolder.loadAvatars(note: Note) { @@ -315,6 +373,7 @@ class NotesAdapter( val threeAvatars2: ImageView val threeAvatars3: ImageView val unreadNotificationView: View + val action: ImageView init { contentView = checkNotNull(view.findViewById(R.id.note_content_container)) @@ -331,6 +390,7 @@ class NotesAdapter( twoAvatarsView = checkNotNull(view.findViewById(R.id.two_avatars_view)) threeAvatarsView = checkNotNull(view.findViewById(R.id.three_avatars_view)) unreadNotificationView = checkNotNull(view.findViewById(R.id.notification_unread)) + action = checkNotNull(view.findViewById(R.id.action)) contentView.setOnClickListener(onClickListener) } } diff --git a/WordPress/src/main/res/drawable/star_filled.xml b/WordPress/src/main/res/drawable/star_filled.xml new file mode 100644 index 000000000000..d17da4965903 --- /dev/null +++ b/WordPress/src/main/res/drawable/star_filled.xml @@ -0,0 +1,9 @@ + + + From 853cf6336083fc57a00897f889aed5b85fcde9b9 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Wed, 14 Feb 2024 21:54:07 +0800 Subject: [PATCH 04/13] Implement the inline action of Like Comment --- .../org/wordpress/android/models/Note.java | 12 +++ .../android/models/NoteExtensions.kt | 1 + .../ui/notifications/adapters/NotesAdapter.kt | 102 +++++++++--------- 3 files changed, 64 insertions(+), 51 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/models/Note.java b/WordPress/src/main/java/org/wordpress/android/models/Note.java index d7c693696067..1e91894cc6fd 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/Note.java +++ b/WordPress/src/main/java/org/wordpress/android/models/Note.java @@ -17,6 +17,7 @@ import org.wordpress.android.fluxc.model.CommentStatus; import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper; import org.wordpress.android.util.AppLog; +import org.wordpress.android.util.AppLog.T; import org.wordpress.android.util.DateTimeUtils; import org.wordpress.android.util.DateUtils; import org.wordpress.android.util.JSONUtils; @@ -522,6 +523,17 @@ public boolean hasLikedComment() { return !(jsonActions == null || jsonActions.length() == 0) && jsonActions.optBoolean(ACTION_KEY_LIKE); } + public void setLikedComment(boolean liked) { + JSONObject jsonActions = getCommentActions(); + if (jsonActions != null) { + try { + jsonActions.put(ACTION_KEY_LIKE, liked); + } catch (JSONException e) { + AppLog.e(T.NOTIFS, "Failed to set 'like' property for the note", e); + } + } + } + public String getUrl() { return queryJSON("url", ""); } diff --git a/WordPress/src/main/java/org/wordpress/android/models/NoteExtensions.kt b/WordPress/src/main/java/org/wordpress/android/models/NoteExtensions.kt index 6685cfffb715..03f6421cc6c8 100644 --- a/WordPress/src/main/java/org/wordpress/android/models/NoteExtensions.kt +++ b/WordPress/src/main/java/org/wordpress/android/models/NoteExtensions.kt @@ -23,6 +23,7 @@ sealed class Notification { NoteType.Like -> Like(url = rawNote.url, title = rawNote.title) NoteType.Reblog -> Reblog(url= rawNote.url, title = rawNote.title) NoteType.NewPost -> NewPost(url= rawNote.url, title = rawNote.title) + NoteType.Comment -> Comment else -> Unknown } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt index 62498034c777..ef133b2f5df3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt @@ -14,19 +14,17 @@ import android.view.ViewGroup.MarginLayoutParams import android.view.ViewTreeObserver import android.widget.ImageView import android.widget.TextView -import androidx.appcompat.content.res.AppCompatResources import androidx.annotation.StringRes import androidx.core.text.BidiFormatter import androidx.core.view.ViewCompat import androidx.core.view.isVisible +import androidx.core.widget.ImageViewCompat import androidx.recyclerview.widget.RecyclerView -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch import org.wordpress.android.R import org.wordpress.android.WordPress import org.wordpress.android.datasets.NotificationsTable @@ -37,7 +35,6 @@ import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.models.Note import org.wordpress.android.models.Note.NoteTimeGroup import org.wordpress.android.models.Note.TimeStampComparator -import org.wordpress.android.models.NoteType import org.wordpress.android.models.Notification import org.wordpress.android.models.Notification.Comment import org.wordpress.android.models.Notification.PostNotification @@ -78,8 +75,10 @@ class NotesAdapter( @Inject lateinit var siteStore: SiteStore + @Inject lateinit var commentStoreAdapter: CommentsStoreAdapter + @Inject lateinit var postLikeUseCase: PostLikeUseCase @@ -292,35 +291,6 @@ class NotesAdapter( } private fun Note.shouldShowMultipleAvatars() = isFollowType || isLikeType || isCommentLikeType - private fun handlePostLikeAction(note: Note, holder: NoteViewHolder) { - val post = ReaderPostTable.getBlogPost(note.siteId.toLong(), note.postId.toLong(), true) ?: return - if(post.canLikePost().not()) return - - val icon = if (post.isLikedByCurrentUser) R.drawable.star_filled else R.drawable.star_empty - GlobalScope.launch { postLikeUseCase.perform(post, !post.isLikedByCurrentUser, "notification") } - } - - private fun handleCommentLikeAction(note: Note, holder: NoteViewHolder) { - val canLike = note.canLike() - if (canLike.not()) return - - val site = siteStore.getSiteBySiteId(note.siteId.toLong())!! - val comment = commentStoreAdapter.getCommentBySiteAndRemoteId(site, note.commentId)!! - val icon = if (comment.iLike) R.drawable.star_filled else R.drawable.star_empty - holder.actionIcon.isVisible = true - holder.actionIcon.setImageDrawable(AppCompatResources.getDrawable(holder.contentView.context, icon)) - holder.actionIcon.setOnClickListener { - commentStoreAdapter.dispatch( - CommentActionBuilder.newLikeCommentAction( - RemoteLikeCommentPayload(site, comment, comment.iLike.not()) - ) - ) - comment.iLike = comment.iLike.not() - val iconId = if (comment.iLike) R.drawable.star_filled else R.drawable.star_empty - holder.actionIcon.setImageDrawable(AppCompatResources.getDrawable(holder.contentView.context, iconId)) - EventBus.getDefault().postSticky(NoteLikeOrModerationStatusChanged(note.id)) - } - } private fun handleMaxLines(subject: TextView, detail: TextView) { subject.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { @@ -407,29 +377,55 @@ class NotesAdapter( @Suppress("ForbiddenComment") fun bindInlineActionIconsForNote(note: Note) = Notification.from(note).let { notification -> when (notification) { - Comment -> { - actionIcon.setImageResource(R.drawable.star_empty) - actionIcon.isVisible = true - actionIcon.setOnClickListener { - // TODO: handle tap on comment's inline action icon (the star) - } - } - is PostNotification -> { - actionIcon.setImageResource(R.drawable.block_share) - actionIcon.isVisible = true - actionIcon.setOnClickListener { - coroutineScope.launch { - inlineActionEvents.emit( - InlineActionEvent.SharePostButtonTapped(notification) - ) - } - } - } + Comment -> bindLikeCommentAction(note) + is PostNotification.NewPost -> bindLikePostAction(note) + is PostNotification -> bindShareAction(notification) is Unknown -> { actionIcon.isVisible = false } } } + + private fun bindShareAction(notification: PostNotification) { + actionIcon.setImageResource(R.drawable.block_share) + actionIcon.isVisible = true + actionIcon.setOnClickListener { + coroutineScope.launch { + inlineActionEvents.emit( + InlineActionEvent.SharePostButtonTapped(notification) + ) + } + } + } + + private fun bindLikePostAction(note: Note) { + val post = ReaderPostTable.getBlogPost(note.siteId.toLong(), note.postId.toLong(), true) ?: return + if (post.canLikePost().not()) return + actionIcon.isVisible = true + val icon = if (post.isLikedByCurrentUser) R.drawable.star_filled else R.drawable.star_empty + actionIcon.setImageResource(icon) +// GlobalScope.launch { postLikeUseCase.perform(post, !post.isLikedByCurrentUser, "notification") } + } + + private fun bindLikeCommentAction(note: Note) { + if (note.canLike().not()) return + val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return + actionIcon.isVisible = true + actionIcon.setImageResource(if (note.hasLikedComment()) R.drawable.star_filled else R.drawable.star_empty) + ImageViewCompat.setImageTintList(actionIcon, null) + + actionIcon.setOnClickListener { + val liked = note.hasLikedComment().not() + commentStoreAdapter.dispatch( + CommentActionBuilder.newLikeCommentAction( + RemoteLikeCommentPayload(site, note.commentId, liked) + ) + ) + note.setLikedComment(liked) + actionIcon.setImageResource(if (liked) R.drawable.star_filled else R.drawable.star_empty) + EventBus.getDefault().postSticky(NoteLikeOrModerationStatusChanged(note.id)) + } + } } private val onClickListener = View.OnClickListener { view -> @@ -471,15 +467,19 @@ class NotesAdapter( FILTERS.FILTER_COMMENT -> if (currentNote.isCommentType) { filteredNotes.add(currentNote) } + FILTERS.FILTER_FOLLOW -> if (currentNote.isFollowType) { filteredNotes.add(currentNote) } + FILTERS.FILTER_UNREAD -> if (currentNote.isUnread) { filteredNotes.add(currentNote) } + FILTERS.FILTER_LIKE -> if (currentNote.isLikeType) { filteredNotes.add(currentNote) } + else -> Unit } } From 9d9584ba4680db2dc50805d3ad03532d3f628096 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 11:08:22 +0800 Subject: [PATCH 05/13] Move functions to view model --- .../NotificationsListFragmentPage.kt | 1 + .../NotificationsListViewModel.kt | 24 ++++++++-- .../ui/notifications/adapters/NotesAdapter.kt | 48 ++++++------------- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt index 1948ca3ccf90..41ec9dd68222 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt @@ -427,6 +427,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l ActivityLauncher.openShareIntent(it, postNotification.url, postNotification.title) } } + is InlineActionEvent.LikeCommentButtonTapped -> viewModel.likeComment(actionEvent.note, actionEvent.liked) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt index 6299411f80d2..c1830c886f73 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt @@ -3,11 +3,15 @@ package org.wordpress.android.ui.notifications import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus import org.wordpress.android.datasets.NotificationsTable +import org.wordpress.android.fluxc.store.CommentsStore +import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.models.Note import org.wordpress.android.models.Notification.PostNotification import org.wordpress.android.modules.UI_THREAD @@ -29,9 +33,11 @@ class NotificationsListViewModel @Inject constructor( private val appPrefsWrapper: AppPrefsWrapper, private val jetpackBrandingUtils: JetpackBrandingUtils, private val jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil, - private val gcmMessageHandler: GCMMessageHandler + private val gcmMessageHandler: GCMMessageHandler, + private val siteStore: SiteStore, + private val commentStore: CommentsStore, -) : ScopedViewModel(mainDispatcher) { + ) : ScopedViewModel(mainDispatcher) { private val _showJetpackPoweredBottomSheet = MutableLiveData>() val showJetpackPoweredBottomSheet: LiveData> = _showJetpackPoweredBottomSheet @@ -81,7 +87,19 @@ class NotificationsListViewModel @Inject constructor( } } + fun likeComment(note: Note, liked: Boolean) { + val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return + viewModelScope.launch { + val result = commentStore.likeComment(site, note.commentId, null, liked) + if (result.isError.not()) { + note.setLikedComment(liked) + NotificationsTable.saveNote(note) + } + } + } + sealed class InlineActionEvent { - data class SharePostButtonTapped(val notification: PostNotification): InlineActionEvent() + data class SharePostButtonTapped(val notification: PostNotification) : InlineActionEvent() + class LikeCommentButtonTapped(val note: Note, val liked: Boolean) : InlineActionEvent() } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt index ef133b2f5df3..5bcbfda7db2c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt @@ -4,6 +4,7 @@ package org.wordpress.android.ui.notifications.adapters import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList import android.os.AsyncTask import android.text.Spanned import android.text.TextUtils @@ -20,18 +21,13 @@ import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.core.widget.ImageViewCompat import androidx.recyclerview.widget.RecyclerView -import kotlinx.coroutines.launch -import org.greenrobot.eventbus.EventBus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch import org.wordpress.android.R import org.wordpress.android.WordPress import org.wordpress.android.datasets.NotificationsTable -import org.wordpress.android.datasets.ReaderPostTable -import org.wordpress.android.fluxc.generated.CommentActionBuilder -import org.wordpress.android.fluxc.store.CommentStore.RemoteLikeCommentPayload -import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.models.Note import org.wordpress.android.models.Note.NoteTimeGroup import org.wordpress.android.models.Note.TimeStampComparator @@ -40,16 +36,14 @@ import org.wordpress.android.models.Notification.Comment import org.wordpress.android.models.Notification.PostNotification import org.wordpress.android.models.Notification.Unknown import org.wordpress.android.ui.comments.CommentUtils -import org.wordpress.android.ui.comments.unified.CommentsStoreAdapter -import org.wordpress.android.ui.notifications.NotificationEvents.NoteLikeOrModerationStatusChanged import org.wordpress.android.ui.notifications.NotificationsListFragmentPage.OnNoteClickListener import org.wordpress.android.ui.notifications.NotificationsListViewModel.InlineActionEvent import org.wordpress.android.ui.notifications.adapters.NotesAdapter.NoteViewHolder import org.wordpress.android.ui.notifications.blocks.NoteBlockClickableSpan import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper -import org.wordpress.android.ui.reader.repository.usecases.PostLikeUseCase import org.wordpress.android.util.GravatarUtils import org.wordpress.android.util.RtlUtils +import org.wordpress.android.util.extensions.getColorFromAttribute import org.wordpress.android.util.image.ImageManager import org.wordpress.android.util.image.ImageType import javax.inject.Inject @@ -73,15 +67,6 @@ class NotesAdapter( @Inject lateinit var notificationsUtilsWrapper: NotificationsUtilsWrapper - @Inject - lateinit var siteStore: SiteStore - - @Inject - lateinit var commentStoreAdapter: CommentsStoreAdapter - - @Inject - lateinit var postLikeUseCase: PostLikeUseCase - enum class FILTERS { FILTER_ALL, FILTER_COMMENT, @@ -378,7 +363,7 @@ class NotesAdapter( fun bindInlineActionIconsForNote(note: Note) = Notification.from(note).let { notification -> when (notification) { Comment -> bindLikeCommentAction(note) - is PostNotification.NewPost -> bindLikePostAction(note) + is PostNotification.NewPost -> bindLikePostAction() is PostNotification -> bindShareAction(notification) is Unknown -> { actionIcon.isVisible = false @@ -388,6 +373,8 @@ class NotesAdapter( private fun bindShareAction(notification: PostNotification) { actionIcon.setImageResource(R.drawable.block_share) + val color = contentView.context.getColorFromAttribute(R.attr.wpColorOnSurfaceMedium) + ImageViewCompat.setImageTintList(actionIcon, ColorStateList.valueOf(color)) actionIcon.isVisible = true actionIcon.setOnClickListener { coroutineScope.launch { @@ -398,32 +385,25 @@ class NotesAdapter( } } - private fun bindLikePostAction(note: Note) { - val post = ReaderPostTable.getBlogPost(note.siteId.toLong(), note.postId.toLong(), true) ?: return - if (post.canLikePost().not()) return - actionIcon.isVisible = true - val icon = if (post.isLikedByCurrentUser) R.drawable.star_filled else R.drawable.star_empty - actionIcon.setImageResource(icon) -// GlobalScope.launch { postLikeUseCase.perform(post, !post.isLikedByCurrentUser, "notification") } + private fun bindLikePostAction() { + // TODO: implement like post action } private fun bindLikeCommentAction(note: Note) { if (note.canLike().not()) return - val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return + actionIcon.isVisible = true actionIcon.setImageResource(if (note.hasLikedComment()) R.drawable.star_filled else R.drawable.star_empty) ImageViewCompat.setImageTintList(actionIcon, null) actionIcon.setOnClickListener { val liked = note.hasLikedComment().not() - commentStoreAdapter.dispatch( - CommentActionBuilder.newLikeCommentAction( - RemoteLikeCommentPayload(site, note.commentId, liked) - ) - ) - note.setLikedComment(liked) actionIcon.setImageResource(if (liked) R.drawable.star_filled else R.drawable.star_empty) - EventBus.getDefault().postSticky(NoteLikeOrModerationStatusChanged(note.id)) + coroutineScope.launch { + inlineActionEvents.emit( + InlineActionEvent.LikeCommentButtonTapped(note, liked) + ) + } } } } From d9279f428b396bcdaedc8fa8ee6b982025aed05f Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 12:54:48 +0800 Subject: [PATCH 06/13] Fix lint issues --- .../android/ui/notifications/adapters/NotesAdapter.kt | 4 +--- WordPress/src/main/res/drawable/star_filled.xml | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt index 5bcbfda7db2c..f180256be155 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt @@ -1,5 +1,4 @@ @file:Suppress("DEPRECATION") - package org.wordpress.android.ui.notifications.adapters import android.annotation.SuppressLint @@ -48,7 +47,6 @@ import org.wordpress.android.util.image.ImageManager import org.wordpress.android.util.image.ImageType import javax.inject.Inject - class NotesAdapter( context: Context, dataLoadedListener: DataLoadedListener, onLoadMoreListener: OnLoadMoreListener?, @@ -386,7 +384,7 @@ class NotesAdapter( } private fun bindLikePostAction() { - // TODO: implement like post action + TODO("implement like post action") } private fun bindLikeCommentAction(note: Note) { diff --git a/WordPress/src/main/res/drawable/star_filled.xml b/WordPress/src/main/res/drawable/star_filled.xml index d17da4965903..84ae3d2e18fd 100644 --- a/WordPress/src/main/res/drawable/star_filled.xml +++ b/WordPress/src/main/res/drawable/star_filled.xml @@ -1,9 +1,9 @@ + android:viewportHeight="24"> From b260d02c13bec6d9254cfafad903fe5fde475fd4 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 13:28:25 +0800 Subject: [PATCH 07/13] Make refactors --- .../ui/notifications/NotificationEvents.java | 12 ++++++++++++ .../NotificationsListFragmentPage.kt | 8 ++++++++ .../notifications/NotificationsListViewModel.kt | 14 +++----------- .../ui/notifications/adapters/NotesAdapter.kt | 17 ++++++++++++++++- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java index 899c697240a7..adbaa4c01d49 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java @@ -1,5 +1,7 @@ package org.wordpress.android.ui.notifications; +import androidx.annotation.NonNull; + import com.android.volley.VolleyError; import org.wordpress.android.models.Note; @@ -25,6 +27,16 @@ public NoteLikeOrModerationStatusChanged(String noteId) { } } + /** + * Event fired when a note is liked or unliked from the Like comment inline action + */ + public static class NoteLikeCommentActionPerformed { + public final Note note; + public NoteLikeCommentActionPerformed(@NonNull final Note note) { + this.note = note; + } + } + public static class NotificationsSettingsStatusChanged { final String mMessage; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt index 41ec9dd68222..461c3c16de83 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt @@ -456,6 +456,14 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l } } + @Subscribe(threadMode = MAIN) + fun onEventMainThread(event: NotificationEvents.NoteLikeCommentActionPerformed) { + if (!isAdded) { + return + } + notesAdapter!!.updateNote(event.note) + } + @Subscribe(threadMode = MAIN) fun onEventMainThread(event: NotificationsChanged) { if (!isAdded) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt index c1830c886f73..4467e92cb717 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt @@ -18,10 +18,10 @@ import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.push.GCMMessageHandler import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalOverlayUtil import org.wordpress.android.ui.jetpackoverlay.JetpackOverlayConnectedFeature.NOTIFICATIONS +import org.wordpress.android.ui.notifications.NotificationEvents.NoteLikeCommentActionPerformed import org.wordpress.android.ui.notifications.NotificationEvents.NotificationsChanged import org.wordpress.android.ui.notifications.utils.NotificationsActions import org.wordpress.android.ui.prefs.AppPrefsWrapper -import org.wordpress.android.util.JetpackBrandingUtils import org.wordpress.android.viewmodel.Event import org.wordpress.android.viewmodel.ScopedViewModel import javax.inject.Inject @@ -31,7 +31,6 @@ import javax.inject.Named class NotificationsListViewModel @Inject constructor( @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher, private val appPrefsWrapper: AppPrefsWrapper, - private val jetpackBrandingUtils: JetpackBrandingUtils, private val jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil, private val gcmMessageHandler: GCMMessageHandler, private val siteStore: SiteStore, @@ -49,14 +48,6 @@ class NotificationsListViewModel @Inject constructor( val isNotificationsPermissionsWarningDismissed get() = appPrefsWrapper.notificationPermissionsWarningDismissed - init { - if (jetpackBrandingUtils.shouldShowJetpackPoweredBottomSheet()) showJetpackPoweredBottomSheet() - } - - private fun showJetpackPoweredBottomSheet() { -// _showJetpackPoweredBottomSheet.value = Event(true) - } - fun onResume() { if (jetpackFeatureRemovalOverlayUtil.shouldShowFeatureSpecificJetpackOverlay(NOTIFICATIONS)) showJetpackOverlay() @@ -89,10 +80,11 @@ class NotificationsListViewModel @Inject constructor( fun likeComment(note: Note, liked: Boolean) { val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return + note.setLikedComment(liked) + EventBus.getDefault().post(NoteLikeCommentActionPerformed(note)) viewModelScope.launch { val result = commentStore.likeComment(site, note.commentId, null, liked) if (result.isError.not()) { - note.setLikedComment(liked) NotificationsTable.saveNote(note) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt index f180256be155..1586d8f0183a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt @@ -306,6 +306,21 @@ class NotesAdapter( reloadNotesFromDBTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) } + /** + * Update the note in the adapter and notify the change + */ + fun updateNote(note: Note) { + val notePosition = notes.indexOfFirst { it.id == note.id } + if (notePosition != -1) { + notes[notePosition] = note + } + val filteredPosition = filteredNotes.indexOfFirst { it.id == note.id } + if (filteredPosition != -1) { + filteredNotes[filteredPosition] = note + notifyItemChanged(filteredPosition) + } + } + @SuppressLint("StaticFieldLeak") private inner class ReloadNotesFromDBTask : AsyncTask>() { override fun doInBackground(vararg voids: Void?): ArrayList { @@ -384,7 +399,7 @@ class NotesAdapter( } private fun bindLikePostAction() { - TODO("implement like post action") + // implement like post action } private fun bindLikeCommentAction(note: Note) { From 587ffbe5d5e395f7f042f8a7aac0a20bb1a3b191 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 14:23:50 +0800 Subject: [PATCH 08/13] Remove an event --- .../android/ui/notifications/NotificationEvents.java | 10 ---------- .../ui/notifications/NotificationsListFragmentPage.kt | 11 +++-------- .../ui/notifications/NotificationsListViewModel.kt | 6 ++++-- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java index adbaa4c01d49..5114e9c45d90 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java @@ -27,16 +27,6 @@ public NoteLikeOrModerationStatusChanged(String noteId) { } } - /** - * Event fired when a note is liked or unliked from the Like comment inline action - */ - public static class NoteLikeCommentActionPerformed { - public final Note note; - public NoteLikeCommentActionPerformed(@NonNull final Note note) { - this.note = note; - } - } - public static class NotificationsSettingsStatusChanged { final String mMessage; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt index 461c3c16de83..0369b2eaa8b1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt @@ -140,6 +140,9 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l layoutNewNotificatons.visibility = View.GONE layoutNewNotificatons.setOnClickListener { onScrollToTop() } } + viewModel.updatedNote.observe(viewLifecycleOwner) { + createOrGetNotesAdapter().updateNote(it) + } } override fun onDestroyView() { @@ -456,14 +459,6 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l } } - @Subscribe(threadMode = MAIN) - fun onEventMainThread(event: NotificationEvents.NoteLikeCommentActionPerformed) { - if (!isAdded) { - return - } - notesAdapter!!.updateNote(event.note) - } - @Subscribe(threadMode = MAIN) fun onEventMainThread(event: NotificationsChanged) { if (!isAdded) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt index 4467e92cb717..ec63d34d090d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt @@ -18,7 +18,6 @@ import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.push.GCMMessageHandler import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalOverlayUtil import org.wordpress.android.ui.jetpackoverlay.JetpackOverlayConnectedFeature.NOTIFICATIONS -import org.wordpress.android.ui.notifications.NotificationEvents.NoteLikeCommentActionPerformed import org.wordpress.android.ui.notifications.NotificationEvents.NotificationsChanged import org.wordpress.android.ui.notifications.utils.NotificationsActions import org.wordpress.android.ui.prefs.AppPrefsWrapper @@ -43,6 +42,9 @@ class NotificationsListViewModel @Inject constructor( private val _showJetpackOverlay = MutableLiveData>() val showJetpackOverlay: LiveData> = _showJetpackOverlay + private val _updatedNote = MutableLiveData() + val updatedNote: LiveData = _updatedNote + val inlineActionEvents = MutableSharedFlow() val isNotificationsPermissionsWarningDismissed @@ -81,7 +83,7 @@ class NotificationsListViewModel @Inject constructor( fun likeComment(note: Note, liked: Boolean) { val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return note.setLikedComment(liked) - EventBus.getDefault().post(NoteLikeCommentActionPerformed(note)) + _updatedNote.postValue(note) viewModelScope.launch { val result = commentStore.likeComment(site, note.commentId, null, liked) if (result.isError.not()) { From 1bce49af0c528c72bd77abb8b7a1c3ae1d9471c3 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 14:45:35 +0800 Subject: [PATCH 09/13] Make refactors --- .../ui/notifications/NotificationEvents.java | 2 - .../NotificationsListFragmentPage.kt | 50 +++++++------------ .../res/layout/notifications_list_item.xml | 1 + 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java index 5114e9c45d90..899c697240a7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationEvents.java @@ -1,7 +1,5 @@ package org.wordpress.android.ui.notifications; -import androidx.annotation.NonNull; - import com.android.volley.VolleyError; import org.wordpress.android.models.Note; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt index 0369b2eaa8b1..c57413e1eebc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt @@ -70,7 +70,7 @@ import javax.inject.Inject class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_list_fragment_page), OnScrollToTopListener, DataLoadedListener { - private var notesAdapter: NotesAdapter? = null + private lateinit var notesAdapter: NotesAdapter private var swipeToRefreshHelper: SwipeToRefreshHelper? = null private var isAnimatingOutNewNotificationsBar = false private var shouldRefreshNotifications = false @@ -95,17 +95,6 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l fun onClickNote(noteId: String?) } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - val adapter = createOrGetNotesAdapter() - binding?.notificationsList?.adapter = adapter - if (savedInstanceState != null) { - tabPosition = savedInstanceState.getInt(KEY_TAB_POSITION, All.ordinal) - } - (TabPosition.values().getOrNull(tabPosition) ?: All).let { adapter.setFilter(it.filter) } - } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == RequestCodes.NOTE_DETAIL) { @@ -131,24 +120,32 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l arguments?.let { tabPosition = it.getInt(KEY_TAB_POSITION, All.ordinal) } + notesAdapter = NotesAdapter( requireActivity(), this, null, + inlineActionEvents = viewModel.inlineActionEvents).apply { + this.setOnNoteClickListener(mOnNoteClickListener) + viewModel.inlineActionEvents.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) + .onEach(::handleInlineActionEvent) + .launchIn(viewLifecycleOwner.lifecycleScope) + } binding = NotificationsListFragmentPageBinding.bind(view).apply { notificationsList.layoutManager = LinearLayoutManager(activity) + notificationsList.adapter = notesAdapter swipeToRefreshHelper = WPSwipeToRefreshHelper.buildSwipeToRefreshHelper(notificationsRefresh) { hideNewNotificationsBar() fetchNotesFromRemote() } layoutNewNotificatons.visibility = View.GONE layoutNewNotificatons.setOnClickListener { onScrollToTop() } + (TabPosition.values().getOrNull(tabPosition) ?: All).let { notesAdapter.setFilter(it.filter) } } viewModel.updatedNote.observe(viewLifecycleOwner) { - createOrGetNotesAdapter().updateNote(it) + notesAdapter.updateNote(it) } } override fun onDestroyView() { super.onDestroyView() - notesAdapter?.cancelReloadNotesTask() - notesAdapter = null + notesAdapter.cancelReloadNotesTask() swipeToRefreshHelper = null binding?.notificationsList?.adapter = null binding?.notificationsList?.removeCallbacks(showNewUnseenNotificationsRunnable) @@ -185,7 +182,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l binding?.hideNewNotificationsBar() EventBus.getDefault().post(NotificationsUnseenStatus(false)) if (accountStore.hasAccessToken()) { - notesAdapter!!.reloadNotesFromDBAsync() + notesAdapter.reloadNotesFromDBAsync() if (shouldRefreshNotifications) { fetchNotesFromRemote() } @@ -232,7 +229,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l // Open the latest version of this note in case it has changed, which can happen if the note was tapped // from the list after it was updated by another fragment (such as NotificationsDetailListFragment). - openNoteForReply(activity, noteId, false, null, notesAdapter!!.currentFilter, false) + openNoteForReply(activity, noteId, false, null, notesAdapter.currentFilter, false) } } private val mOnScrollListener: OnScrollListener = object : OnScrollListener() { @@ -251,7 +248,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l } private fun fetchNotesFromRemote() { - if (!isAdded || notesAdapter == null) { + if (!isAdded) { return } if (!NetworkUtils.isNetworkAvailable(activity)) { @@ -412,17 +409,6 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l } } - private fun createOrGetNotesAdapter(): NotesAdapter { - return notesAdapter ?: NotesAdapter( requireActivity(), this, null, - inlineActionEvents = viewModel.inlineActionEvents).apply { - notesAdapter = this - this.setOnNoteClickListener(mOnNoteClickListener) - viewModel.inlineActionEvents.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) - .onEach(::handleInlineActionEvent) - .launchIn(viewLifecycleOwner.lifecycleScope) - } - } - private fun handleInlineActionEvent(actionEvent: InlineActionEvent) { when (actionEvent) { is SharePostButtonTapped -> actionEvent.notification.let { postNotification -> @@ -439,7 +425,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l * Mark notifications as read in CURRENT tab, use filteredNotes instead of notes */ fun markAllNotesAsRead() { - viewModel.markNoteAsRead(requireContext(), createOrGetNotesAdapter().filteredNotes) + viewModel.markNoteAsRead(requireContext(), notesAdapter.filteredNotes) } @Subscribe(sticky = true, threadMode = MAIN) @@ -464,7 +450,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l if (!isAdded) { return } - notesAdapter!!.reloadNotesFromDBAsync() + notesAdapter.reloadNotesFromDBAsync() if (event.hasUnseenNotes) { binding?.showNewUnseenNotificationsUI() } @@ -476,7 +462,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l return } swipeToRefreshHelper?.isRefreshing = false - notesAdapter!!.addAll(event.notes, true) + notesAdapter.addAll(event.notes, true) } @Suppress("unused", "UNUSED_PARAMETER") diff --git a/WordPress/src/main/res/layout/notifications_list_item.xml b/WordPress/src/main/res/layout/notifications_list_item.xml index fc63f6e86a9f..106ba0000f78 100644 --- a/WordPress/src/main/res/layout/notifications_list_item.xml +++ b/WordPress/src/main/res/layout/notifications_list_item.xml @@ -116,6 +116,7 @@ android:ellipsize="end" android:importantForAccessibility="no" android:maxLines="2" + android:gravity="start" android:textAppearance="@style/WordPress.TextAppearance.NotificationItemContent" android:visibility="gone" app:layout_goneMarginEnd="@dimen/margin_extra_large" From b81d9967f78cc11260415a884803aa423bc48618 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 14:51:50 +0800 Subject: [PATCH 10/13] Fix an issue related to RTL --- WordPress/src/main/res/layout/notifications_list_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/res/layout/notifications_list_item.xml b/WordPress/src/main/res/layout/notifications_list_item.xml index 106ba0000f78..640df7fde81b 100644 --- a/WordPress/src/main/res/layout/notifications_list_item.xml +++ b/WordPress/src/main/res/layout/notifications_list_item.xml @@ -116,7 +116,7 @@ android:ellipsize="end" android:importantForAccessibility="no" android:maxLines="2" - android:gravity="start" + android:textAlignment="viewStart" android:textAppearance="@style/WordPress.TextAppearance.NotificationItemContent" android:visibility="gone" app:layout_goneMarginEnd="@dimen/margin_extra_large" From 8a68798688a2d03410586560d6bf9336ae79ddf9 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 20:11:42 +0800 Subject: [PATCH 11/13] Update color settings --- .../ui/notifications/adapters/NotesAdapter.kt | 17 +++++++++++------ WordPress/src/main/res/values-night/colors.xml | 2 ++ WordPress/src/main/res/values/colors.xml | 3 +++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt index 1586d8f0183a..0d5dd3fc4ad1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/adapters/NotesAdapter.kt @@ -1,4 +1,5 @@ @file:Suppress("DEPRECATION") + package org.wordpress.android.ui.notifications.adapters import android.annotation.SuppressLint @@ -404,14 +405,10 @@ class NotesAdapter( private fun bindLikeCommentAction(note: Note) { if (note.canLike().not()) return - - actionIcon.isVisible = true - actionIcon.setImageResource(if (note.hasLikedComment()) R.drawable.star_filled else R.drawable.star_empty) - ImageViewCompat.setImageTintList(actionIcon, null) - + setupLikeIcon(note.hasLikedComment()) actionIcon.setOnClickListener { val liked = note.hasLikedComment().not() - actionIcon.setImageResource(if (liked) R.drawable.star_filled else R.drawable.star_empty) + setupLikeIcon(liked) coroutineScope.launch { inlineActionEvents.emit( InlineActionEvent.LikeCommentButtonTapped(note, liked) @@ -419,6 +416,14 @@ class NotesAdapter( } } } + + private fun setupLikeIcon(liked: Boolean) { + actionIcon.isVisible = true + actionIcon.setImageResource(if (liked) R.drawable.star_filled else R.drawable.star_empty) + val color = if (liked) contentView.context.getColor(R.color.inline_action_filled) + else contentView.context.getColorFromAttribute(R.attr.wpColorOnSurfaceMedium) + ImageViewCompat.setImageTintList(actionIcon, ColorStateList.valueOf(color)) + } } private val onClickListener = View.OnClickListener { view -> diff --git a/WordPress/src/main/res/values-night/colors.xml b/WordPress/src/main/res/values-night/colors.xml index 4fed9a9fe846..045ca78257e4 100644 --- a/WordPress/src/main/res/values-night/colors.xml +++ b/WordPress/src/main/res/values-night/colors.xml @@ -115,4 +115,6 @@ @color/black @color/white + + @color/jetpack_green_30 diff --git a/WordPress/src/main/res/values/colors.xml b/WordPress/src/main/res/values/colors.xml index 0697863412fc..8a503bb522a4 100644 --- a/WordPress/src/main/res/values/colors.xml +++ b/WordPress/src/main/res/values/colors.xml @@ -151,4 +151,7 @@ #DEDEDE #8A8A8E + + + @color/jetpack_green_50 From 050cc68101555ca54460b3e56b7a18f7fc7ad4b2 Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 20:38:08 +0800 Subject: [PATCH 12/13] Update the coroutine dispatcher settings --- .../ui/notifications/NotificationsListViewModel.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt index ec63d34d090d..463b09103ff5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt @@ -3,18 +3,16 @@ package org.wordpress.android.ui.notifications import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus import org.wordpress.android.datasets.NotificationsTable import org.wordpress.android.fluxc.store.CommentsStore import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.models.Note import org.wordpress.android.models.Notification.PostNotification -import org.wordpress.android.modules.UI_THREAD +import org.wordpress.android.modules.BG_THREAD import org.wordpress.android.push.GCMMessageHandler import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalOverlayUtil import org.wordpress.android.ui.jetpackoverlay.JetpackOverlayConnectedFeature.NOTIFICATIONS @@ -28,14 +26,13 @@ import javax.inject.Named @HiltViewModel class NotificationsListViewModel @Inject constructor( - @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher, + @Named(BG_THREAD) bgDispatcher: CoroutineDispatcher, private val appPrefsWrapper: AppPrefsWrapper, private val jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil, private val gcmMessageHandler: GCMMessageHandler, private val siteStore: SiteStore, - private val commentStore: CommentsStore, - - ) : ScopedViewModel(mainDispatcher) { + private val commentStore: CommentsStore +) : ScopedViewModel(bgDispatcher) { private val _showJetpackPoweredBottomSheet = MutableLiveData>() val showJetpackPoweredBottomSheet: LiveData> = _showJetpackPoweredBottomSheet @@ -84,7 +81,7 @@ class NotificationsListViewModel @Inject constructor( val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return note.setLikedComment(liked) _updatedNote.postValue(note) - viewModelScope.launch { + launch { val result = commentStore.likeComment(site, note.commentId, null, liked) if (result.isError.not()) { NotificationsTable.saveNote(note) From 2b40d4ca5b8ce1a48f15398db23621383217301a Mon Sep 17 00:00:00 2001 From: Jarvis Lin Date: Thu, 15 Feb 2024 20:55:02 +0800 Subject: [PATCH 13/13] Make the coroutine block cover the entire function --- .../notifications/NotificationsListViewModel.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt index 463b09103ff5..2307deba635e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableSharedFlow import org.greenrobot.eventbus.EventBus import org.wordpress.android.datasets.NotificationsTable +import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.CommentsStore import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.models.Note @@ -77,15 +78,16 @@ class NotificationsListViewModel @Inject constructor( } } - fun likeComment(note: Note, liked: Boolean) { - val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: return + fun likeComment(note: Note, liked: Boolean) = launch { + val site = siteStore.getSiteBySiteId(note.siteId.toLong()) ?: SiteModel().apply { + siteId = note.siteId.toLong() + setIsWPCom(true) + } note.setLikedComment(liked) _updatedNote.postValue(note) - launch { - val result = commentStore.likeComment(site, note.commentId, null, liked) - if (result.isError.not()) { - NotificationsTable.saveNote(note) - } + val result = commentStore.likeComment(site, note.commentId, null, liked) + if (result.isError.not()) { + NotificationsTable.saveNote(note) } }