Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package org.ole.planet.myplanet.repository

data class NotificationData(
val type: String,
val message: String,
val relatedId: String?
)

interface NotificationRepository {
suspend fun getUnreadCount(userId: String?): Int
suspend fun updateResourceNotification(userId: String?, resourceCount: Int)
Expand All @@ -11,4 +17,9 @@ interface NotificationRepository {
relatedId: String?,
userId: String?,
)
suspend fun createNotificationsBatch(
notifications: List<NotificationData>,
userId: String?
)
suspend fun cleanupDuplicateNotifications(userId: String?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,34 +50,95 @@ class NotificationRepositoryImpl @Inject constructor(
}.toInt()
}

override suspend fun createNotificationsBatch(
notifications: List<NotificationData>,
userId: String?
) {
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] createNotificationsBatch called: ${notifications.size} notifications for user $userId")
if (notifications.isEmpty() || userId == null) return

val actualUserId = userId

// Pre-fetch all existing notifications for this user in a single query
val existingNotifications = withRealmAsync { realm ->
realm.where(RealmNotification::class.java)
.equalTo("userId", actualUserId)
.findAll()
.map { notif ->
// Create a unique key for fast lookup
val key = "${notif.type}:${notif.relatedId ?: "null"}"
key to notif
}
.toMap()
}
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] Found ${existingNotifications.size} existing notifications in DB: ${existingNotifications.keys}")

// Filter out notifications that already exist
val notificationsToCreate = notifications.filter { notif ->
val key = "${notif.type}:${notif.relatedId ?: "null"}"
!existingNotifications.containsKey(key)
}
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] After filtering: ${notificationsToCreate.size} new notifications to create")

// Create notifications in smaller batches for better performance
if (notificationsToCreate.isNotEmpty()) {
val chunkSize = 10
val chunks = notificationsToCreate.chunked(chunkSize)
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] Split into ${chunks.size} chunks of max $chunkSize")

chunks.forEachIndexed { index, chunk ->
executeTransaction { realm ->
chunk.forEach { notif ->
realm.createObject(RealmNotification::class.java, UUID.randomUUID().toString()).apply {
this.userId = actualUserId
this.type = notif.type
this.message = notif.message
this.relatedId = notif.relatedId
this.createdAt = Date()
}
}
}
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] Created chunk ${index + 1}/${chunks.size} (${chunk.size} notifications)")
}
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] Created ${notificationsToCreate.size} notifications in DB")
} else {
android.util.Log.d("NotifTiming", "[${System.currentTimeMillis()}] No new notifications to create")
}
}

override suspend fun updateResourceNotification(userId: String?, resourceCount: Int) {
userId ?: return

val notificationId = "$userId:resource:count"
val existingNotification = findByField(RealmNotification::class.java, "id", notificationId)

if (resourceCount > 0) {
val previousCount = existingNotification?.message?.toIntOrNull() ?: 0
val countChanged = previousCount != resourceCount
executeTransaction { realm ->
val existingNotification = realm.where(RealmNotification::class.java)
.equalTo("id", notificationId)
.findFirst()

val notification = existingNotification?.apply {
message = "$resourceCount"
relatedId = "$resourceCount"
if (countChanged) {
this.isRead = false
this.createdAt = Date()
if (resourceCount > 0) {
val previousCount = existingNotification?.message?.toIntOrNull() ?: 0
val countChanged = previousCount != resourceCount

if (existingNotification != null) {
existingNotification.message = "$resourceCount"
existingNotification.relatedId = "$resourceCount"
if (countChanged) {
existingNotification.isRead = false
existingNotification.createdAt = Date()
}
} else {
realm.createObject(RealmNotification::class.java, notificationId).apply {
this.userId = userId
this.type = "resource"
this.message = "$resourceCount"
this.relatedId = "$resourceCount"
this.createdAt = Date()
}
}
} ?: RealmNotification().apply {
this.id = notificationId
this.userId = userId
this.type = "resource"
this.message = "$resourceCount"
this.relatedId = "$resourceCount"
this.createdAt = Date()
} else {
existingNotification?.deleteFromRealm()
}
save(notification)
} else {
existingNotification?.let { delete(RealmNotification::class.java, "id", it.id) }
}
}

Expand Down Expand Up @@ -115,5 +176,41 @@ class NotificationRepositoryImpl @Inject constructor(
}
return updatedIds
}

override suspend fun cleanupDuplicateNotifications(userId: String?) {
if (userId == null) return

// Get all notifications for this user
val allNotifications = withRealmAsync { realm ->
realm.where(RealmNotification::class.java)
.equalTo("userId", userId)
.findAll()
.let { realm.copyFromRealm(it) }
}

// Group by type+relatedId, keep only the most recent one
val notificationsToKeep = allNotifications
.groupBy { "${it.type}:${it.relatedId ?: "null"}" }
.mapValues { (_, notifications) ->
// Keep the most recent one (latest createdAt)
notifications.maxByOrNull { it.createdAt?.time ?: 0L }
}
.values
.filterNotNull()

val idsToKeep = notificationsToKeep.map { it.id }.toSet()
val idsToDelete = allNotifications.map { it.id }.filter { !idsToKeep.contains(it) }

if (idsToDelete.isNotEmpty()) {
executeTransaction { realm ->
idsToDelete.forEach { idToDelete ->
realm.where(RealmNotification::class.java)
.equalTo("id", idToDelete)
.findFirst()
?.deleteFromRealm()
}
}
}
}
}

Loading