From ba518269b47af5c705eed157982146e1ad3950c7 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sun, 7 Sep 2025 09:04:21 +0200 Subject: [PATCH] fix geohash livedata wiring (#389) --- .../android/nostr/NostrGeohashService.kt | 83 ++++--------------- .../java/com/bitchat/android/ui/ChatScreen.kt | 9 +- .../com/bitchat/android/ui/ChatViewModel.kt | 7 -- .../com/bitchat/android/ui/MessageManager.kt | 14 +++- 4 files changed, 31 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/nostr/NostrGeohashService.kt b/app/src/main/java/com/bitchat/android/nostr/NostrGeohashService.kt index cb98f732..f59e4434 100644 --- a/app/src/main/java/com/bitchat/android/nostr/NostrGeohashService.kt +++ b/app/src/main/java/com/bitchat/android/nostr/NostrGeohashService.kt @@ -87,10 +87,8 @@ class NostrGeohashService( private var geohashSamplingJob: Job? = null private var geoParticipantsTimer: Job? = null - // MARK: - Geohash Message History Properties - - private val geohashMessageHistory = mutableMapOf>() // geohash -> messages - private val maxGeohashMessages = 1000 // Maximum messages per geohash + // MARK: - Geohash Message Routing + // Geohash messages are routed through MessageManager channel timelines using key "geo:" // MARK: - Location Channel Management Properties @@ -166,7 +164,6 @@ class NostrGeohashService( try { geohashParticipants.clear() geoNicknames.clear() - geohashMessageHistory.clear() processedNostrEvents.clear() processedNostrEventOrder.clear() currentGeohashSubscriptionId = null @@ -258,10 +255,8 @@ class NostrGeohashService( powDifficulty = if (powSettingsLocal.enabled) powSettingsLocal.difficulty else null ) - // Store immediately; UI will display from geohash history (not main mesh timeline) - storeGeohashMessage(channel.geohash, localMessage) - // IMPORTANT: Do not add to main mesh timeline to avoid duplication in mesh chat view - // messageManager.addMessage(localMessage) + // Local echo into geohash channel timeline via unified MessageManager + messageManager.addChannelMessage("geo:${channel.geohash}", localMessage) Log.d(TAG, "📝 Added geohash local echo with temp ID: $tempMessageId (not shown in mesh timeline)") @@ -692,55 +687,7 @@ class NostrGeohashService( } } - // MARK: - Geohash Message History - - /** - * Store a message in geohash history - */ - private fun storeGeohashMessage(geohash: String, message: BitchatMessage) { - val messages = geohashMessageHistory.getOrPut(geohash) { mutableListOf() } - messages.add(message) - - // Limit message history to prevent memory issues - if (messages.size > maxGeohashMessages) { - messages.removeAt(0) // Remove oldest message - } - - Log.v(TAG, "📦 Stored message in geohash $geohash history (${messages.size} total) - sender: ${message.sender}, content: '${message.content.take(30)}...'") - } - - /** - * Load stored messages for a geohash channel - */ - fun loadGeohashMessages(geohash: String) { - val storedMessages = geohashMessageHistory[geohash] - if (storedMessages == null) { - Log.d(TAG, "📥 No stored messages found for geohash $geohash") - return - } - - Log.d(TAG, "📥 Loading ${storedMessages.size} stored messages for geohash $geohash") - - // Add all stored messages to the current message timeline - storedMessages.forEach { message -> - Log.v(TAG, "📥 Loading stored message: ${message.sender} - '${message.content.take(30)}...'") - messageManager.addMessage(message) - } - } - - /** - * Get stored messages for a geohash without mutating UI state - */ - fun getGeohashMessages(geohash: String): List { - return geohashMessageHistory[geohash]?.toList() ?: emptyList() - } - - /** - * Clear geohash message history - */ - fun clearGeohashMessageHistory() { - geohashMessageHistory.clear() - } + // Geohash message history is maintained in ChatState.channelMessages; no local cache here // MARK: - Geohash Participant Tracking @@ -994,8 +941,7 @@ class NostrGeohashService( private fun switchLocationChannel(channel: com.bitchat.android.geohash.ChannelID?) { // STEP 1: Immediate UI updates (synchronous, no blocking) try { - // NOTE: Don't clear messages here - let ChatScreen's displayMessages logic handle what to show - // This preserves mesh message history when switching between views + // Do not clear messages here - preserve mesh history when switching between views when (channel) { is com.bitchat.android.geohash.ChannelID.Mesh -> { @@ -1020,7 +966,10 @@ class NostrGeohashService( // Clear notifications for this geohash since user is now viewing it notificationManager.clearNotificationsForGeohash(channel.channel.geohash) // Note: Don't clear geoNicknames - they contain cached nicknames for all geohashes - // Note: Don't load messages here - ChatScreen will get them via getGeohashMessages() + // Clear unread for this geohash since user is now viewing it + try { + messageManager.clearChannelUnreadCount("geo:${channel.channel.geohash}") + } catch (_: Exception) { } // Immediate self-registration for instant UI feedback try { @@ -1300,11 +1249,8 @@ class NostrGeohashService( powDifficulty = actualPow.takeIf { it > 0 && eventHasNonseTag } ?: null ) - // Store in geohash history for persistence across channel switches - storeGeohashMessage(geohash, message) - - // NOTE: Don't add to main message timeline here - ChatScreen will display geohash messages - // from the separate geohash history via getGeohashMessages() + // Add to geohash channel timeline (unified pipeline) + withContext(Dispatchers.Main) { messageManager.addChannelMessage("geo:$geohash", message) } // NOTIFICATION LOGIC: Check for mentions and first messages checkAndTriggerGeohashNotifications(geohash, senderName, content, message) @@ -1403,8 +1349,9 @@ class NostrGeohashService( * Check if this is the first message in a subscribed geohash chat */ private fun checkIfFirstMessage(geohash: String, message: BitchatMessage): Boolean { - // Get the message history for this geohash - val messageHistory = geohashMessageHistory[geohash] ?: return true + // Use ChatState.channelMessages for geohash history + val channelKey = "geo:$geohash" + val messageHistory = state.getChannelMessagesValue()[channelKey] ?: emptyList() // Filter out our own messages (local echoes and messages from our identity) val otherUserMessages = messageHistory.filter { msg -> diff --git a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt index 6c0a5555..8e4e43b5 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt @@ -74,18 +74,17 @@ fun ChatScreen(viewModel: ChatViewModel) { // Get location channel info for timeline switching val selectedLocationChannel by viewModel.selectedLocationChannel.observeAsState() - // Determine what messages to show based on current context + // Determine what messages to show based on current context (unified timelines) val displayMessages = when { selectedPrivatePeer != null -> privateChats[selectedPrivatePeer] ?: emptyList() currentChannel != null -> channelMessages[currentChannel] ?: emptyList() else -> { val locationChannel = selectedLocationChannel if (locationChannel is com.bitchat.android.geohash.ChannelID.Location) { - // For geohash channels, get messages from geohash history - val geohash = locationChannel.channel.geohash - viewModel.getGeohashMessages(geohash) + val geokey = "geo:${locationChannel.channel.geohash}" + channelMessages[geokey] ?: emptyList() } else { - messages // Mesh/public messages + messages // Mesh timeline } } } diff --git a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt index 74302cb9..89429b53 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt @@ -649,13 +649,6 @@ class ChatViewModel( } } - /** - * Get messages for a specific geohash timeline - */ - fun getGeohashMessages(geohash: String): List { - return nostrGeohashService.getGeohashMessages(geohash) - } - /** * Get participant count for a specific geohash (5-minute activity window) */ diff --git a/app/src/main/java/com/bitchat/android/ui/MessageManager.kt b/app/src/main/java/com/bitchat/android/ui/MessageManager.kt index e554679d..276a6bbe 100644 --- a/app/src/main/java/com/bitchat/android/ui/MessageManager.kt +++ b/app/src/main/java/com/bitchat/android/ui/MessageManager.kt @@ -52,8 +52,18 @@ class MessageManager(private val state: ChatState) { currentChannelMessages[channel] = channelMessageList state.setChannelMessages(currentChannelMessages) - // Update unread count if not currently in this channel - if (state.getCurrentChannelValue() != channel) { + // Update unread count if not currently viewing this channel + // Consider both classic channels (state.currentChannel) and geohash location channel selection + val viewingClassicChannel = state.getCurrentChannelValue() == channel + val viewingGeohashChannel = try { + if (channel.startsWith("geo:")) { + val geo = channel.removePrefix("geo:") + val selected = state.selectedLocationChannel.value + selected is com.bitchat.android.geohash.ChannelID.Location && selected.channel.geohash.equals(geo, ignoreCase = true) + } else false + } catch (_: Exception) { false } + + if (!viewingClassicChannel && !viewingGeohashChannel) { val currentUnread = state.getUnreadChannelMessagesValue().toMutableMap() currentUnread[channel] = (currentUnread[channel] ?: 0) + 1 state.setUnreadChannelMessages(currentUnread)