Optimize Notifcation loading
Auto Tag on Version Change / check-version (push) Successful in 3s

This commit is contained in:
2026-06-10 13:46:18 +05:00
parent 570e6b750b
commit 80bbacc130
2 changed files with 150 additions and 5 deletions
@@ -13,10 +13,12 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
@@ -62,6 +64,23 @@ private fun toGroupedList(notifications: List<AppNotification>): List<NotifListI
return result
}
private class NotifDiff(
private val old: List<NotifListItem>,
private val new: List<NotifListItem>
) : DiffUtil.Callback() {
override fun getOldListSize() = old.size
override fun getNewListSize() = new.size
override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
val o = old[oldPos]; val n = new[newPos]
return when {
o is NotifListItem.Header && n is NotifListItem.Header -> o.label == n.label
o is NotifListItem.Entry && n is NotifListItem.Entry -> o.n.id == n.n.id
else -> false
}
}
override fun areContentsTheSame(oldPos: Int, newPos: Int) = old[oldPos] == new[newPos]
}
class NotificationsSheetFragment : BottomSheetDialogFragment() {
var onUnreadCountChanged: ((hasUnread: Boolean) -> Unit)? = null
@@ -148,11 +167,16 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
}
}
private fun setSpinner(show: Boolean) {
tabAdapters.forEach { it?.showLoadingSpinner = show }
}
private fun refreshFromNetwork() {
val bmlSessions = app.bmlSessions.toMap()
val mibSessions = app.mibSessions.toMap()
lifecycleScope.launch {
setSpinner(true)
val bmlClient = BmlNotificationsClient()
bmlSessions.forEach { (loginId, session) ->
val result = withContext(Dispatchers.IO) {
@@ -196,6 +220,8 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
mibDone[loginId] = hasOverlap || result.nextStart > result.totalCount
}
}
if (isAdded) setSpinner(false)
}
}
@@ -208,6 +234,7 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
if (!anyLeft) return
isLoadingMore = true
setSpinner(true)
lifecycleScope.launch {
val bmlClient = BmlNotificationsClient()
bmlSessions.forEach { (loginId, session) ->
@@ -255,7 +282,10 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
}
isLoadingMore = false
if (isAdded) refreshAdapters()
if (isAdded) {
setSpinner(false)
refreshAdapters()
}
}
}
@@ -359,16 +389,35 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
private val displayItems = mutableListOf<NotifListItem>()
var showLoadingSpinner: Boolean = false
set(value) {
if (field == value) return
field = value
if (displayItems.isEmpty()) {
notifyItemChanged(0)
} else if (value) {
notifyItemInserted(displayItems.size)
} else {
notifyItemRemoved(displayItems.size)
}
}
fun update(filtered: List<AppNotification>) {
val newItems = toGroupedList(filtered)
val diff = DiffUtil.calculateDiff(NotifDiff(displayItems.toList(), newItems))
displayItems.clear()
displayItems.addAll(toGroupedList(filtered))
notifyDataSetChanged()
displayItems.addAll(newItems)
diff.dispatchUpdatesTo(this)
}
override fun getItemCount() = if (displayItems.isEmpty()) 1 else displayItems.size
override fun getItemCount(): Int {
if (displayItems.isEmpty()) return 1
return displayItems.size + if (showLoadingSpinner) 1 else 0
}
override fun getItemViewType(position: Int): Int {
if (displayItems.isEmpty()) return 2 // empty
if (displayItems.isEmpty()) return if (showLoadingSpinner) 3 else 2
if (showLoadingSpinner && position == displayItems.size) return 3
return when (displayItems[position]) {
is NotifListItem.Header -> 0
is NotifListItem.Entry -> 1
@@ -379,6 +428,7 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
when (viewType) {
0 -> HeaderVH(buildHeaderView(parent.context))
1 -> ItemVH(buildRowView(parent.context))
3 -> SpinnerVH(buildSpinnerView(parent.context))
else -> EmptyVH(buildEmptyView(parent.context))
}
@@ -439,6 +489,28 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
}
}
// ── Loading spinner ───────────────────────────────────────────────────────
inner class SpinnerVH(v: View) : RecyclerView.ViewHolder(v)
private fun buildSpinnerView(ctx: android.content.Context): View {
val dp = ctx.resources.displayMetrics.density
val pad = (16 * dp).toInt()
val size = (28 * dp).toInt()
return LinearLayout(ctx).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
setPadding(pad, pad, pad, pad)
addView(ProgressBar(ctx).apply {
layoutParams = LinearLayout.LayoutParams(size, size)
})
}
}
// ── Notification row ──────────────────────────────────────────────────────
inner class ItemVH(v: View) : RecyclerView.ViewHolder(v) {