diff --git a/app/src/main/java/com/bitchat/android/nostr/GeohashMessageHandler.kt b/app/src/main/java/com/bitchat/android/nostr/GeohashMessageHandler.kt index 338efd85..0e6e6634 100644 --- a/app/src/main/java/com/bitchat/android/nostr/GeohashMessageHandler.kt +++ b/app/src/main/java/com/bitchat/android/nostr/GeohashMessageHandler.kt @@ -22,7 +22,8 @@ class GeohashMessageHandler( private val state: ChatState, private val messageManager: MessageManager, private val repo: GeohashRepository, - private val scope: CoroutineScope + private val scope: CoroutineScope, + private val dataManager: com.bitchat.android.ui.DataManager ) { companion object { private const val TAG = "GeohashMessageHandler" } @@ -56,8 +57,8 @@ class GeohashMessageHandler( if (!NostrProofOfWork.validateDifficulty(event, pow.difficulty)) return@launch } - // Blocked users check - if (com.bitchat.android.ui.DataManager(application).isGeohashUserBlocked(event.pubkey)) return@launch + // Blocked users check (use injected DataManager which has loaded state) + if (dataManager.isGeohashUserBlocked(event.pubkey)) return@launch // Update repository (participants, nickname, teleport) // Update repository on a background-safe path; repository will post updates to LiveData diff --git a/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt b/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt index f5e607bc..c72c8e67 100644 --- a/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt +++ b/app/src/main/java/com/bitchat/android/nostr/GeohashRepository.kt @@ -14,7 +14,8 @@ import java.util.Date */ class GeohashRepository( private val application: Application, - private val state: ChatState + private val state: ChatState, + private val dataManager: com.bitchat.android.ui.DataManager ) { companion object { private const val TAG = "GeohashRepository" } @@ -103,7 +104,8 @@ class GeohashRepository( val e = it.next() if (e.value.before(cutoff)) it.remove() } - return participants.size + // exclude blocked users + return participants.keys.count { !dataManager.isGeohashUserBlocked(it) } } fun refreshGeohashPeople() { @@ -122,8 +124,18 @@ class GeohashRepository( if (e.value.before(cutoff)) it.remove() } geohashParticipants[geohash] = participants - val people = participants.map { (pubkeyHex, lastSeen) -> - val base = getCachedNickname(pubkeyHex) ?: "anon" + // exclude blocked users from people list + val people = participants.filterKeys { !dataManager.isGeohashUserBlocked(it) } + .map { (pubkeyHex, lastSeen) -> + // Use our actual nickname for self; otherwise use cached nickname or anon + val base = try { + val myHex = currentGeohash?.let { NostrIdentityBridge.deriveIdentity(it, application).publicKeyHex } + if (myHex != null && myHex.equals(pubkeyHex, true)) { + state.getNicknameValue() ?: "anon" + } else { + getCachedNickname(pubkeyHex) ?: "anon" + } + } catch (_: Exception) { getCachedNickname(pubkeyHex) ?: "anon" } GeoPerson( id = pubkeyHex.lowercase(), displayName = base, // UI can add #hash if necessary @@ -138,7 +150,8 @@ class GeohashRepository( val cutoff = Date(System.currentTimeMillis() - 5 * 60 * 1000) val counts = mutableMapOf() for ((gh, participants) in geohashParticipants) { - val active = participants.values.count { !it.before(cutoff) } + val active = participants.filterKeys { !dataManager.isGeohashUserBlocked(it) } + .values.count { !it.before(cutoff) } counts[gh] = active } // Use postValue for thread safety - this can be called from background threads @@ -186,6 +199,7 @@ class GeohashRepository( val participants = geohashParticipants[current] ?: emptyMap() var count = 0 for ((k, t) in participants) { + if (dataManager.isGeohashUserBlocked(k)) continue if (t.before(cutoff)) continue val name = if (k.equals(lower, true)) base else (geoNicknames[k.lowercase()] ?: "anon") if (name.equals(base, true)) { count++; if (count > 1) break } @@ -207,6 +221,7 @@ class GeohashRepository( val participants = geohashParticipants[sourceGeohash] ?: emptyMap() var count = 0 for ((k, t) in participants) { + if (dataManager.isGeohashUserBlocked(k)) continue if (t.before(cutoff)) continue val name = if (k.equals(lower, true)) base else (geoNicknames[k.lowercase()] ?: "anon") if (name.equals(base, true)) { count++; if (count > 1) break } diff --git a/app/src/main/java/com/bitchat/android/nostr/NostrDirectMessageHandler.kt b/app/src/main/java/com/bitchat/android/nostr/NostrDirectMessageHandler.kt index 1de7c70b..da8dbf7a 100644 --- a/app/src/main/java/com/bitchat/android/nostr/NostrDirectMessageHandler.kt +++ b/app/src/main/java/com/bitchat/android/nostr/NostrDirectMessageHandler.kt @@ -21,7 +21,8 @@ class NostrDirectMessageHandler( private val privateChatManager: PrivateChatManager, private val meshDelegateHandler: MeshDelegateHandler, private val scope: CoroutineScope, - private val repo: GeohashRepository + private val repo: GeohashRepository, + private val dataManager: com.bitchat.android.ui.DataManager ) { companion object { private const val TAG = "NostrDirectMessageHandler" } @@ -58,6 +59,10 @@ class NostrDirectMessageHandler( } val (content, senderPubkey, rumorTimestamp) = decryptResult + + // If sender is blocked for geohash contexts, drop any events from this pubkey + // Applies to both geohash DMs (geohash != "") and account DMs (geohash == "") + if (dataManager.isGeohashUserBlocked(senderPubkey)) return@launch if (!content.startsWith("bitchat1:")) return@launch val base64Content = content.removePrefix("bitchat1:") diff --git a/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt b/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt index b0e740db..bce3ef1c 100644 --- a/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt +++ b/app/src/main/java/com/bitchat/android/ui/AboutSheet.kt @@ -415,7 +415,7 @@ fun AboutSheet( horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically ) { - Text("Tor On", fontFamily = FontFamily.Monospace) + Text("tor on", fontFamily = FontFamily.Monospace) val statusColor = when { torStatus.running && torStatus.bootstrapPercent < 100 -> Color(0xFFFF9500) torStatus.running && torStatus.bootstrapPercent >= 100 -> if (isDark) Color(0xFF32D74B) else Color(0xFF248A3D) diff --git a/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt b/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt index 6a9e4ee2..060bb84e 100644 --- a/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/GeohashViewModel.kt @@ -30,10 +30,25 @@ class GeohashViewModel( companion object { private const val TAG = "GeohashViewModel" } - private val repo = GeohashRepository(application, state) + private val repo = GeohashRepository(application, state, dataManager) private val subscriptionManager = NostrSubscriptionManager(application, viewModelScope) - private val geohashMessageHandler = GeohashMessageHandler(application, state, messageManager, repo, viewModelScope) - private val dmHandler = NostrDirectMessageHandler(application, state, privateChatManager, meshDelegateHandler, viewModelScope, repo) + private val geohashMessageHandler = GeohashMessageHandler( + application = application, + state = state, + messageManager = messageManager, + repo = repo, + scope = viewModelScope, + dataManager = dataManager + ) + private val dmHandler = NostrDirectMessageHandler( + application = application, + state = state, + privateChatManager = privateChatManager, + meshDelegateHandler = meshDelegateHandler, + scope = viewModelScope, + repo = repo, + dataManager = dataManager + ) private var currentGeohashSubId: String? = null private var currentDmSubId: String? = null @@ -141,25 +156,6 @@ class GeohashViewModel( fun geohashParticipantCount(geohash: String): Int = repo.geohashParticipantCount(geohash) fun isPersonTeleported(pubkeyHex: String): Boolean = repo.isPersonTeleported(pubkeyHex) - fun startGeohashDM(pubkeyHex: String, onStartPrivateChat: (String) -> Unit) { - val convKey = "nostr_${pubkeyHex.take(16)}" - repo.putNostrKeyMapping(convKey, pubkeyHex) - onStartPrivateChat(convKey) - Log.d(TAG, "🗨️ Started geohash DM with $pubkeyHex -> $convKey") - } - - fun getNostrKeyMapping(): Map = repo.getNostrKeyMapping() - - fun blockUserInGeohash(targetNickname: String) { - val pubkey = repo.findPubkeyByNickname(targetNickname) - if (pubkey != null) { - dataManager.addGeohashBlockedUser(pubkey) - val sysMsg = com.bitchat.android.model.BitchatMessage( - sender = "system", - content = "blocked $targetNickname in geohash channels", - timestamp = Date(), - isRelay = false - ) fun startGeohashDM(pubkeyHex: String, onStartPrivateChat: (String) -> Unit) { val convKey = "nostr_${pubkeyHex.take(16)}" repo.putNostrKeyMapping(convKey, pubkeyHex) @@ -174,6 +170,21 @@ class GeohashViewModel( Log.d(TAG, "🗨️ Started geohash DM with ${pubkeyHex} -> ${convKey} (geohash=${gh})") } + fun getNostrKeyMapping(): Map = repo.getNostrKeyMapping() + + fun blockUserInGeohash(targetNickname: String) { + val pubkey = repo.findPubkeyByNickname(targetNickname) + if (pubkey != null) { + dataManager.addGeohashBlockedUser(pubkey) + // Refresh people list and counts to remove blocked entry immediately + repo.refreshGeohashPeople() + repo.updateReactiveParticipantCounts() + val sysMsg = com.bitchat.android.model.BitchatMessage( + sender = "system", + content = "blocked $targetNickname in geohash channels", + timestamp = Date(), + isRelay = false + ) messageManager.addMessage(sysMsg) } else { val sysMsg = com.bitchat.android.model.BitchatMessage(