fix: more robust notification loading
This commit is contained in:
parent
9471683501
commit
7b1f1a1357
4 changed files with 63 additions and 53 deletions
|
@ -135,7 +135,6 @@ class App : MultiDexApplication() {
|
||||||
|
|
||||||
androidx.work.WorkManager.getInstance(this)
|
androidx.work.WorkManager.getInstance(this)
|
||||||
.enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::class.java))
|
.enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::class.java))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1403,7 +1403,7 @@ Page(page:$page,perPage:50) {
|
||||||
suspend fun getNotifications(id: Int, page: Int = 1, resetNotification: Boolean = true): NotificationResponse? {
|
suspend fun getNotifications(id: Int, page: Int = 1, resetNotification: Boolean = true): NotificationResponse? {
|
||||||
val reset = if (resetNotification) "true" else "false"
|
val reset = if (resetNotification) "true" else "false"
|
||||||
val res = executeQuery<NotificationResponse>(
|
val res = executeQuery<NotificationResponse>(
|
||||||
"""{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
|
force = true
|
||||||
)
|
)
|
||||||
if (res != null && resetNotification) {
|
if (res != null && resetNotification) {
|
||||||
|
|
|
@ -47,6 +47,8 @@ data class NotificationUser(
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class NotificationPage(
|
data class NotificationPage(
|
||||||
|
@SerialName("pageInfo")
|
||||||
|
val pageInfo: PageInfo,
|
||||||
@SerialName("notifications")
|
@SerialName("notifications")
|
||||||
val notifications: List<Notification>,
|
val notifications: List<Notification>,
|
||||||
) : java.io.Serializable
|
) : java.io.Serializable
|
||||||
|
|
|
@ -12,7 +12,6 @@ import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.AnilistQueries
|
|
||||||
import ani.dantotsu.connections.anilist.api.Notification
|
import ani.dantotsu.connections.anilist.api.Notification
|
||||||
import ani.dantotsu.databinding.ActivityFollowBinding
|
import ani.dantotsu.databinding.ActivityFollowBinding
|
||||||
import ani.dantotsu.initActivity
|
import ani.dantotsu.initActivity
|
||||||
|
@ -30,13 +29,13 @@ import com.xwray.groupie.GroupieAdapter
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class NotificationActivity : AppCompatActivity() {
|
class NotificationActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityFollowBinding
|
private lateinit var binding: ActivityFollowBinding
|
||||||
private var adapter: GroupieAdapter = GroupieAdapter()
|
private var adapter: GroupieAdapter = GroupieAdapter()
|
||||||
private var notificationList: List<Notification> = emptyList()
|
private var notificationList: List<Notification> = emptyList()
|
||||||
private var page: Int = 1
|
private var currentPage: Int = 1
|
||||||
|
private var hasNextPage: Boolean = true
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
|
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -63,19 +62,59 @@ class NotificationActivity : AppCompatActivity() {
|
||||||
binding.listProgressBar.visibility = ViewGroup.VISIBLE
|
binding.listProgressBar.visibility = ViewGroup.VISIBLE
|
||||||
val activityId = intent.getIntExtra("activityId", -1)
|
val activityId = intent.getIntExtra("activityId", -1)
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
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 (hasNextPage && !binding.listRecyclerView.canScrollVertically(1) && !binding.followRefresh.isVisible
|
||||||
|
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
|
||||||
|
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
|
||||||
|
) {
|
||||||
|
binding.followRefresh.visibility = ViewGroup.VISIBLE
|
||||||
|
loadPage(-1) {
|
||||||
|
binding.followRefresh.visibility = ViewGroup.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.log("No more notifications")
|
||||||
|
snackString("No more notifications")
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.followSwipeRefresh.setOnRefreshListener {
|
||||||
|
currentPage = 1
|
||||||
|
hasNextPage = true
|
||||||
|
adapter.clear()
|
||||||
|
notificationList = emptyList()
|
||||||
|
loadPage(-1) {
|
||||||
|
binding.followSwipeRefresh.isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadPage(activityId: Int, onFinish: () -> Unit = {}) {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val resetNotification = activityId == -1
|
val resetNotification = activityId == -1
|
||||||
val res = Anilist.query.getNotifications(
|
val res = Anilist.query.getNotifications(
|
||||||
Anilist.userid ?: PrefManager.getVal<String>(PrefName.AnilistUserId).toIntOrNull()
|
Anilist.userid ?: PrefManager.getVal<String>(PrefName.AnilistUserId).toIntOrNull()
|
||||||
?: 0,
|
?: 0, currentPage, resetNotification = resetNotification
|
||||||
resetNotification = resetNotification
|
|
||||||
)
|
)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
val newNotifications: MutableList<Notification> = mutableListOf()
|
||||||
res?.data?.page?.notifications?.let { notifications ->
|
res?.data?.page?.notifications?.let { notifications ->
|
||||||
Logger.log("Notifications: $notifications")
|
Logger.log("Notifications: $notifications")
|
||||||
notificationList = if (activityId != -1) {
|
newNotifications += if (activityId != -1) {
|
||||||
notifications.filter { it.id == activityId }
|
notifications.filter { it.id == activityId }
|
||||||
} else {
|
} else {
|
||||||
notifications
|
notifications
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
|
}
|
||||||
|
if (activityId == -1 && currentPage == 1) {
|
||||||
val commentStore = PrefManager.getNullableVal<List<CommentStore>>(
|
val commentStore = PrefManager.getNullableVal<List<CommentStore>>(
|
||||||
PrefName.CommentNotificationStore,
|
PrefName.CommentNotificationStore,
|
||||||
null
|
null
|
||||||
|
@ -90,50 +129,19 @@ class NotificationActivity : AppCompatActivity() {
|
||||||
context = it.title + "\n" + it.content,
|
context = it.title + "\n" + it.content,
|
||||||
createdAt = (it.time / 1000L).toInt(),
|
createdAt = (it.time / 1000L).toInt(),
|
||||||
)
|
)
|
||||||
notificationList = notificationList + notification
|
newNotifications += notification
|
||||||
}
|
}
|
||||||
notificationList = notificationList.sortedByDescending { it.createdAt }
|
|
||||||
|
|
||||||
adapter.update(notificationList.map { NotificationItem(it, ::onNotificationClick) })
|
|
||||||
}
|
|
||||||
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
|
|
||||||
&& binding.listRecyclerView.adapter!!.itemCount != 0 &&
|
|
||||||
(binding.listRecyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() == (binding.listRecyclerView.adapter!!.itemCount - 1)
|
|
||||||
) {
|
|
||||||
page++
|
|
||||||
binding.followRefresh.visibility = ViewGroup.VISIBLE
|
|
||||||
loadPage {
|
|
||||||
binding.followRefresh.visibility = ViewGroup.GONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.followSwipeRefresh.setOnRefreshListener {
|
notificationList += newNotifications
|
||||||
page = 1
|
adapter.addAll(newNotifications.map {
|
||||||
adapter.clear()
|
NotificationItem(
|
||||||
notificationList = emptyList()
|
it,
|
||||||
loadPage()
|
::onNotificationClick
|
||||||
}
|
)
|
||||||
}
|
})
|
||||||
}
|
currentPage = res?.data?.page?.pageInfo?.currentPage?.plus(1) ?: 1
|
||||||
}
|
hasNextPage = res?.data?.page?.pageInfo?.hasNextPage ?: false
|
||||||
private fun loadPage(onFinish: () -> Unit = {}) {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val res = Anilist.query.getNotifications(Anilist.userid ?: 0, page)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
res?.data?.page?.notifications?.let { notifications ->
|
|
||||||
notificationList += notifications
|
|
||||||
adapter.addAll(notifications.map { NotificationItem(it, ::onNotificationClick) })
|
|
||||||
}
|
|
||||||
binding.followSwipeRefresh.isRefreshing = false
|
binding.followSwipeRefresh.isRefreshing = false
|
||||||
onFinish()
|
onFinish()
|
||||||
}
|
}
|
||||||
|
@ -164,7 +172,8 @@ class NotificationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationClickType.COMMENT -> {
|
NotificationClickType.COMMENT -> {
|
||||||
ContextCompat.startActivity(this, Intent(this, MediaDetailsActivity::class.java)
|
ContextCompat.startActivity(
|
||||||
|
this, Intent(this, MediaDetailsActivity::class.java)
|
||||||
.putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
|
.putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
|
||||||
.putExtra("mediaId", id)
|
.putExtra("mediaId", id)
|
||||||
.putExtra("commentId", optional ?: -1),
|
.putExtra("commentId", optional ?: -1),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue