Skip to content
This repository was archived by the owner on May 23, 2025. It is now read-only.

Commit 774b5a9

Browse files
committed
818: Track favorites and filter old duplicated entries
1 parent 4d61135 commit 774b5a9

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,13 @@ class NotificationsFragment :
205205
(binding.recyclerView.itemAnimator as SimpleItemAnimator?)!!.supportsChangeAnimations =
206206
false
207207

208-
// Signal the user that a refresh has loaded new items above their current position
209-
// by scrolling up slightly to disclose the new content
208+
210209
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
211210
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
211+
removeDuplicateOlderEntries(positionStart)
212+
213+
// Signal the user that a refresh has loaded new items above their current position
214+
// by scrolling up slightly to disclose the new content
212215
if (positionStart == 0 && adapter.itemCount != itemCount) {
213216
binding.recyclerView.post {
214217
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
@@ -233,6 +236,7 @@ class NotificationsFragment :
233236
viewModel.pagingData.collectLatest { pagingData ->
234237
Log.d(TAG, "Submitting data to adapter")
235238
adapter.submitData(pagingData)
239+
// TODO why is this called always _before_ anything from NotificationsPagingSource is loaded (and with what data)?
236240
}
237241
}
238242

@@ -420,6 +424,27 @@ class NotificationsFragment :
420424
}
421425
}
422426

427+
private fun removeDuplicateOlderEntries(positionStart: Int) {
428+
val dataList = adapter.snapshot().items
429+
430+
Log.w(TAG, "found elements in adapter "+dataList.size)
431+
432+
for (pos in dataList.size - 1 downTo positionStart) {
433+
val notificationViewData = dataList[pos]
434+
435+
val status = notificationViewData.statusViewData?.status
436+
?: continue
437+
438+
if (!viewModel.hasNewestNotificationId(notificationViewData.type, status.id, notificationViewData.id)) {
439+
Log.w(TAG, "Removing old notification at "+pos+" for "+status.id)
440+
441+
adapter.notifyItemRemoved(pos)
442+
}
443+
}
444+
445+
Log.w(TAG, "elements after second "+ adapter.snapshot().size)
446+
}
447+
423448
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
424449
menuInflater.inflate(R.menu.fragment_notifications, menu)
425450
menu.findItem(R.id.action_refresh)?.apply {

app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModel.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.lifecycle.ViewModel
2424
import androidx.lifecycle.viewModelScope
2525
import androidx.paging.PagingData
2626
import androidx.paging.cachedIn
27+
import androidx.paging.filter
2728
import androidx.paging.map
2829
import com.keylesspalace.tusky.R
2930
import com.keylesspalace.tusky.appstore.BlockEvent
@@ -482,13 +483,28 @@ class NotificationsViewModel @Inject constructor(
482483
)
483484
}
484485

486+
// Status id -> (highest) Notification id
487+
val seenFavorites = HashMap<String, String>()
488+
485489
private fun getNotifications(
486490
filters: Set<Notification.Type>,
487491
initialKey: String? = null
488492
): Flow<PagingData<NotificationViewData>> {
489493
return repository.getNotificationsStream(filter = filters, initialKey = initialKey)
490494
.map { pagingData ->
491-
pagingData.map { notification ->
495+
pagingData.filter { notification ->
496+
val status = notification.status
497+
?: return@filter true
498+
499+
return@filter if (hasNewestNotificationId(notification.type, status.id, notification.id)) {
500+
seenFavorites[status.id] = notification.id // TODO move to hasNewestNotificationId?
501+
502+
true
503+
} else {
504+
false
505+
}
506+
}
507+
.map { notification ->
492508
notification.toViewData(
493509
isShowingContent = statusDisplayOptions.value.showSensitiveMedia ||
494510
!(notification.status?.actionableStatus?.sensitive ?: false),
@@ -499,6 +515,29 @@ class NotificationsViewModel @Inject constructor(
499515
}
500516
}
501517

518+
fun hasNewestNotificationId(type: Notification.Type, statusId: String, notificationId: String): Boolean {
519+
if (type != Notification.Type.FAVOURITE) {
520+
return true
521+
}
522+
523+
val highestNotificationId = seenFavorites[statusId]
524+
525+
return highestNotificationId == null || isEqualOrNewer(notificationId, highestNotificationId)
526+
}
527+
528+
/**
529+
* NOTE this currently assumes that the ids are integers. Can that change? If it does all notifications are unequal.
530+
*/
531+
fun isEqualOrNewer(thisId: String, idToCompare: String): Boolean {
532+
try {
533+
return thisId.toInt() >= idToCompare.toInt()
534+
} catch (exc: NumberFormatException) {
535+
Log.e(TAG, "Cannot compare ids; not numbers: "+thisId+"/"+idToCompare)
536+
537+
return false
538+
}
539+
}
540+
502541
/**
503542
* @return Flow of relevant preferences that change the UI
504543
*/

0 commit comments

Comments
 (0)