mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 22:25:19 +00:00
fix: geohash online count accuracy and channel switch latency (#672)
- Keep max lastSeen per user so relay's newest-first delivery doesn't overwrite a fresh timestamp with a stale one - Serialize event processing on Main dispatcher to eliminate ConcurrentModificationException on geohashParticipants maps - Immediately refresh people list on channel switch so sampling data is reflected before the first relay response arrives
This commit is contained in:
@@ -6,9 +6,7 @@ import com.bitchat.android.model.BitchatMessage
|
||||
import com.bitchat.android.ui.ChatState
|
||||
import com.bitchat.android.ui.MessageManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
@@ -44,7 +42,7 @@ class GeohashMessageHandler(
|
||||
}
|
||||
|
||||
fun onEvent(event: NostrEvent, subscribedGeohash: String) {
|
||||
scope.launch(Dispatchers.Default) {
|
||||
scope.launch {
|
||||
try {
|
||||
if (event.kind != NostrKind.EPHEMERAL_EVENT && event.kind != NostrKind.GEOHASH_PRESENCE) return@launch
|
||||
val tagGeo = event.tags.firstOrNull { it.size >= 2 && it[0] == "g" }?.getOrNull(1)
|
||||
@@ -65,9 +63,6 @@ class GeohashMessageHandler(
|
||||
// Blocked users check (use injected DataManager which has loaded state)
|
||||
if (dataManager.isGeohashUserBlocked(pubkey)) return@launch
|
||||
|
||||
// Update repository (participants, nickname, teleport)
|
||||
// Update repository on a background-safe path; repository will post updates to LiveData
|
||||
|
||||
// Update participant count (last seen) on BOTH Presence (20001) and Chat (20000) events
|
||||
if (event.kind == NostrKind.GEOHASH_PRESENCE || event.kind == NostrKind.EPHEMERAL_EVENT) {
|
||||
repo.updateParticipant(subscribedGeohash, pubkey, Date(event.createdAt * 1000L))
|
||||
@@ -107,7 +102,7 @@ class GeohashMessageHandler(
|
||||
if (hasNonce) NostrProofOfWork.calculateDifficulty(event.id).takeIf { it > 0 } else null
|
||||
} catch (_: Exception) { null }
|
||||
)
|
||||
withContext(Dispatchers.Main) { messageManager.addChannelMessage("geo:$subscribedGeohash", msg) }
|
||||
messageManager.addChannelMessage("geo:$subscribedGeohash", msg)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "onEvent error: ${e.message}")
|
||||
}
|
||||
|
||||
@@ -103,7 +103,16 @@ class GeohashRepository(
|
||||
|
||||
fun updateParticipant(geohash: String, participantId: String, lastSeen: Date) {
|
||||
val participants = geohashParticipants.getOrPut(geohash) { mutableMapOf() }
|
||||
participants[participantId] = lastSeen
|
||||
// Cap to now: prevents future-timestamped events (clock skew / malicious created_at)
|
||||
// from pinning lastSeen and blocking subsequent normal heartbeats.
|
||||
// Also keeps max: relays send events newest-first, so subsequent older events for
|
||||
// the same user must not overwrite a fresher lastSeen.
|
||||
val now = Date()
|
||||
val effective = if (lastSeen.after(now)) now else lastSeen
|
||||
val existing = participants[participantId]
|
||||
if (existing == null || effective.after(existing)) {
|
||||
participants[participantId] = effective
|
||||
}
|
||||
if (currentGeohash == geohash) refreshGeohashPeople()
|
||||
updateReactiveParticipantCounts()
|
||||
}
|
||||
|
||||
@@ -367,6 +367,7 @@ class GeohashViewModel(
|
||||
Log.d(TAG, "📍 Switching to geohash channel: ${channel.channel.geohash}")
|
||||
activeChannelGeohash = channel.channel.geohash
|
||||
repo.setCurrentGeohash(channel.channel.geohash)
|
||||
repo.refreshGeohashPeople()
|
||||
notificationManager.setCurrentGeohash(channel.channel.geohash)
|
||||
notificationManager.clearNotificationsForGeohash(channel.channel.geohash)
|
||||
try { messageManager.clearChannelUnreadCount("geo:${channel.channel.geohash}") } catch (_: Exception) { }
|
||||
|
||||
Reference in New Issue
Block a user