From 7b1f1a135717024fd41a2dad6c4a23374a06e3cb Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:02:52 -0500 Subject: [PATCH] fix: more robust notification loading --- app/src/main/java/ani/dantotsu/App.kt | 1 - .../connections/anilist/AnilistQueries.kt | 2 +- .../connections/anilist/api/Notification.kt | 2 + .../profile/activity/NotificationActivity.kt | 111 ++++++++++-------- 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt index 56967c54..7fa5a5a3 100644 --- a/app/src/main/java/ani/dantotsu/App.kt +++ b/app/src/main/java/ani/dantotsu/App.kt @@ -135,7 +135,6 @@ class App : MultiDexApplication() { androidx.work.WorkManager.getInstance(this) .enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::class.java)) - } diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index 6b1bc6d7..a779c2e1 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -1403,7 +1403,7 @@ Page(page:$page,perPage:50) { suspend fun getNotifications(id: Int, page: Int = 1, resetNotification: Boolean = true): NotificationResponse? { val reset = if (resetNotification) "true" else "false" val res = executeQuery( - """{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){notifications(resetNotificationCount:$reset){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""", + """{User(id:$id){unreadNotificationCount}Page(page:$page,perPage:$ITEMS_PER_PAGE){pageInfo{currentPage,hasNextPage}notifications(resetNotificationCount:$reset){__typename...on AiringNotification{id,type,animeId,episode,contexts,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}},}...on FollowingNotification{id,userId,type,context,createdAt,user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMessageNotification{id,userId,type,activityId,context,createdAt,message{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityMentionNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplySubscribedNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ActivityReplyLikeNotification{id,userId,type,activityId,context,createdAt,activity{__typename}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentMentionNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentReplyNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentSubscribedNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadCommentLikeNotification{id,userId,type,commentId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on ThreadLikeNotification{id,userId,type,threadId,context,createdAt,thread{id}comment{id}user{id,name,bannerImage,avatar{medium,large,}}}...on RelatedMediaAdditionNotification{id,type,context,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDataChangeNotification{id,type,mediaId,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaMergeNotification{id,type,mediaId,deletedMediaTitles,context,reason,createdAt,media{id,title{romaji,english,native,userPreferred}bannerImage,coverImage{medium,large}}}...on MediaDeletionNotification{id,type,deletedMediaTitle,context,reason,createdAt,}}}}""", force = true ) if (res != null && resetNotification) { diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt index f526078a..b7d1cf0c 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Notification.kt @@ -47,6 +47,8 @@ data class NotificationUser( @Serializable data class NotificationPage( + @SerialName("pageInfo") + val pageInfo: PageInfo, @SerialName("notifications") val notifications: List, ) : java.io.Serializable diff --git a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt index 4c21bd48..2d85febc 100644 --- a/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/activity/NotificationActivity.kt @@ -12,7 +12,6 @@ import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ani.dantotsu.connections.anilist.Anilist -import ani.dantotsu.connections.anilist.AnilistQueries import ani.dantotsu.connections.anilist.api.Notification import ani.dantotsu.databinding.ActivityFollowBinding import ani.dantotsu.initActivity @@ -30,13 +29,13 @@ import com.xwray.groupie.GroupieAdapter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.Locale class NotificationActivity : AppCompatActivity() { private lateinit var binding: ActivityFollowBinding private var adapter: GroupieAdapter = GroupieAdapter() private var notificationList: List = emptyList() - private var page: Int = 1 + private var currentPage: Int = 1 + private var hasNextPage: Boolean = true @SuppressLint("SetTextI18n", "ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { @@ -63,77 +62,86 @@ class NotificationActivity : AppCompatActivity() { binding.listProgressBar.visibility = ViewGroup.VISIBLE val activityId = intent.getIntExtra("activityId", -1) lifecycleScope.launch { - val resetNotification = activityId == -1 - val res = Anilist.query.getNotifications( - Anilist.userid ?: PrefManager.getVal(PrefName.AnilistUserId).toIntOrNull() - ?: 0, - resetNotification = resetNotification - ) - res?.data?.page?.notifications?.let { notifications -> - Logger.log("Notifications: $notifications") - notificationList = if (activityId != -1) { - notifications.filter { it.id == activityId } - } else { - notifications - }.toMutableList() - val commentStore = PrefManager.getNullableVal>( - PrefName.CommentNotificationStore, - null - ) ?: listOf() - commentStore.forEach { - val notification = Notification( - "COMMENT_REPLY", - System.currentTimeMillis().toInt(), - commentId = it.commentId, - notificationType = "COMMENT_REPLY", - mediaId = it.mediaId, - context = it.title + "\n" + it.content, - createdAt = (it.time / 1000L).toInt(), - ) - notificationList = notificationList + notification - } - notificationList = notificationList.sortedByDescending { it.createdAt } - - adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) }) + loadPage(activityId) { + binding.listProgressBar.visibility = ViewGroup.GONE } withContext(Dispatchers.Main) { binding.listProgressBar.visibility = ViewGroup.GONE binding.listRecyclerView.setOnTouchListener { _, event -> if (event?.action == MotionEvent.ACTION_UP) { - if (adapter.itemCount % AnilistQueries.ITEMS_PER_PAGE != 0) { - //snackString("No more notifications") fix spam? - Logger.log("No more notifications") - } else if (!binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible + if (hasNextPage && !binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible && binding.listRecyclerView.adapter!!.itemCount != 0 && (binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1) ) { - page++ binding.followRefresh.visibility = ViewGroup.VISIBLE - loadPage { + loadPage(-1) { binding.followRefresh.visibility = ViewGroup.GONE } } + Logger.log("No more notifications") + snackString("No more notifications") } false } binding.followSwipeRefresh.setOnRefreshListener { - page = 1 + currentPage = 1 + hasNextPage = true adapter.clear() notificationList = emptyList() - loadPage() + loadPage(-1) { + binding.followSwipeRefresh.isRefreshing = false + } } } } } - private fun loadPage(onFinish: () -> Unit = {}) { + + private fun loadPage(activityId: Int, onFinish: () -> Unit = {}) { lifecycleScope.launch(Dispatchers.IO) { - val res = Anilist.query.getNotifications(Anilist.userid ?: 0, page) + val resetNotification = activityId == -1 + val res = Anilist.query.getNotifications( + Anilist.userid ?: PrefManager.getVal(PrefName.AnilistUserId).toIntOrNull() + ?: 0, currentPage, resetNotification = resetNotification + ) withContext(Dispatchers.Main) { + val newNotifications: MutableList = mutableListOf() res?.data?.page?.notifications?.let { notifications -> - notificationList += notifications - adapter.addAll(notifications.map { NotificationItem(it, ::onNotificationClick) }) + Logger.log("Notifications: $notifications") + newNotifications += if (activityId != -1) { + notifications.filter { it.id == activityId } + } else { + notifications + }.toMutableList() } + if (activityId == -1 && currentPage == 1) { + val commentStore = PrefManager.getNullableVal>( + PrefName.CommentNotificationStore, + null + ) ?: listOf() + commentStore.forEach { + val notification = Notification( + "COMMENT_REPLY", + System.currentTimeMillis().toInt(), + commentId = it.commentId, + notificationType = "COMMENT_REPLY", + mediaId = it.mediaId, + context = it.title + "\n" + it.content, + createdAt = (it.time / 1000L).toInt(), + ) + newNotifications += notification + } + } + + notificationList += newNotifications + adapter.addAll(newNotifications.map { + NotificationItem( + it, + ::onNotificationClick + ) + }) + currentPage = res?.data?.page?.pageInfo?.currentPage?.plus(1) ?: 1 + hasNextPage = res?.data?.page?.pageInfo?.hasNextPage ?: false binding.followSwipeRefresh.isRefreshing = false onFinish() } @@ -164,10 +172,11 @@ class NotificationActivity : AppCompatActivity() { } NotificationClickType.COMMENT -> { - ContextCompat.startActivity(this, Intent(this, MediaDetailsActivity::class.java) - .putExtra("FRAGMENT_TO_LOAD", "COMMENTS") - .putExtra("mediaId", id) - .putExtra("commentId", optional ?: -1), + ContextCompat.startActivity( + this, Intent(this, MediaDetailsActivity::class.java) + .putExtra("FRAGMENT_TO_LOAD", "COMMENTS") + .putExtra("mediaId", id) + .putExtra("commentId", optional ?: -1), null )