mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 01:05:20 +00:00
fix nostr private messages processing (#563)
* fix nostr private messages processing * persisted geohash registry --------- Co-authored-by: a1denvalu3 <>
This commit is contained in:
co-authored by
a1denvalu3 <>
parent
cbe7a2fc95
commit
c7e20a9590
@@ -41,6 +41,12 @@ class BitchatApplication : Application() {
|
|||||||
// Initialize debug preference manager (persists debug toggles)
|
// Initialize debug preference manager (persists debug toggles)
|
||||||
try { com.bitchat.android.ui.debug.DebugPreferenceManager.init(this) } catch (_: Exception) { }
|
try { com.bitchat.android.ui.debug.DebugPreferenceManager.init(this) } catch (_: Exception) { }
|
||||||
|
|
||||||
|
// Initialize Geohash Registries for persistence
|
||||||
|
try {
|
||||||
|
com.bitchat.android.nostr.GeohashAliasRegistry.initialize(this)
|
||||||
|
com.bitchat.android.nostr.GeohashConversationRegistry.initialize(this)
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
// Initialize mesh service preferences
|
// Initialize mesh service preferences
|
||||||
try { com.bitchat.android.service.MeshServicePreferences.init(this) } catch (_: Exception) { }
|
try { com.bitchat.android.service.MeshServicePreferences.init(this) } catch (_: Exception) { }
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,40 @@
|
|||||||
package com.bitchat.android.nostr
|
package com.bitchat.android.nostr
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GeohashAliasRegistry
|
* GeohashAliasRegistry
|
||||||
* - Global, thread-safe registry for alias->Nostr pubkey mappings (e.g., nostr_<pub16> -> pubkeyHex)
|
* - Global, thread-safe registry for alias->Nostr pubkey mappings (e.g., nostr_<pub16> -> pubkeyHex)
|
||||||
* - Allows non-UI components (e.g., MessageRouter) to resolve geohash DM aliases without depending on UI ViewModels
|
* - Persisted to SharedPreferences to survive app restarts.
|
||||||
*/
|
*/
|
||||||
object GeohashAliasRegistry {
|
object GeohashAliasRegistry {
|
||||||
private val map: MutableMap<String, String> = ConcurrentHashMap()
|
private val map: MutableMap<String, String> = ConcurrentHashMap()
|
||||||
|
private const val PREFS_NAME = "geohash_alias_registry"
|
||||||
|
private var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun initialize(context: Context) {
|
||||||
|
if (prefs == null) {
|
||||||
|
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
loadFromPrefs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadFromPrefs() {
|
||||||
|
prefs?.let { p ->
|
||||||
|
val allEntries = p.all
|
||||||
|
for ((key, value) in allEntries) {
|
||||||
|
if (key is String && value is String) {
|
||||||
|
map[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun put(alias: String, pubkeyHex: String) {
|
fun put(alias: String, pubkeyHex: String) {
|
||||||
map[alias] = pubkeyHex
|
map[alias] = pubkeyHex
|
||||||
|
prefs?.edit()?.putString(alias, pubkeyHex)?.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(alias: String): String? = map[alias]
|
fun get(alias: String): String? = map[alias]
|
||||||
@@ -20,5 +43,8 @@ object GeohashAliasRegistry {
|
|||||||
|
|
||||||
fun snapshot(): Map<String, String> = HashMap(map)
|
fun snapshot(): Map<String, String> = HashMap(map)
|
||||||
|
|
||||||
fun clear() { map.clear() }
|
fun clear() {
|
||||||
|
map.clear()
|
||||||
|
prefs?.edit()?.clear()?.apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,51 @@
|
|||||||
package com.bitchat.android.nostr
|
package com.bitchat.android.nostr
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GeohashConversationRegistry
|
* GeohashConversationRegistry
|
||||||
* - Global, thread-safe registry of conversationKey (e.g., "nostr_<pub16>") -> source geohash
|
* - Global, thread-safe registry of conversationKey (e.g., "nostr_<pub16>") -> source geohash
|
||||||
* - Enables routing geohash DMs from anywhere by providing the correct geohash identity
|
* - Enables routing geohash DMs from anywhere by providing the correct geohash identity
|
||||||
|
* - Persisted to SharedPreferences to survive app restarts.
|
||||||
*/
|
*/
|
||||||
object GeohashConversationRegistry {
|
object GeohashConversationRegistry {
|
||||||
private val map = ConcurrentHashMap<String, String>()
|
private val map = ConcurrentHashMap<String, String>()
|
||||||
|
private const val PREFS_NAME = "geohash_conversation_registry"
|
||||||
|
private var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
|
fun initialize(context: Context) {
|
||||||
|
if (prefs == null) {
|
||||||
|
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
loadFromPrefs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadFromPrefs() {
|
||||||
|
prefs?.let { p ->
|
||||||
|
val allEntries = p.all
|
||||||
|
for ((key, value) in allEntries) {
|
||||||
|
if (key is String && value is String) {
|
||||||
|
map[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun set(convKey: String, geohash: String) {
|
fun set(convKey: String, geohash: String) {
|
||||||
if (geohash.isNotEmpty()) map[convKey] = geohash
|
if (geohash.isNotEmpty()) {
|
||||||
|
map[convKey] = geohash
|
||||||
|
prefs?.edit()?.putString(convKey, geohash)?.apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(convKey: String): String? = map[convKey]
|
fun get(convKey: String): String? = map[convKey]
|
||||||
|
|
||||||
fun snapshot(): Map<String, String> = map.toMap()
|
fun snapshot(): Map<String, String> = map.toMap()
|
||||||
|
|
||||||
fun clear() = map.clear()
|
fun clear() {
|
||||||
|
map.clear()
|
||||||
|
prefs?.edit()?.clear()?.apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import com.bitchat.android.nostr.NostrProtocol
|
|||||||
import com.bitchat.android.nostr.NostrRelayManager
|
import com.bitchat.android.nostr.NostrRelayManager
|
||||||
import com.bitchat.android.nostr.NostrSubscriptionManager
|
import com.bitchat.android.nostr.NostrSubscriptionManager
|
||||||
import com.bitchat.android.nostr.PoWPreferenceManager
|
import com.bitchat.android.nostr.PoWPreferenceManager
|
||||||
|
import com.bitchat.android.nostr.GeohashAliasRegistry
|
||||||
|
import com.bitchat.android.nostr.GeohashConversationRegistry
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -93,6 +95,8 @@ class GeohashViewModel(
|
|||||||
|
|
||||||
fun panicReset() {
|
fun panicReset() {
|
||||||
repo.clearAll()
|
repo.clearAll()
|
||||||
|
GeohashAliasRegistry.clear()
|
||||||
|
GeohashConversationRegistry.clear()
|
||||||
subscriptionManager.disconnect()
|
subscriptionManager.disconnect()
|
||||||
currentGeohashSubId = null
|
currentGeohashSubId = null
|
||||||
currentDmSubId = null
|
currentDmSubId = null
|
||||||
@@ -168,7 +172,7 @@ class GeohashViewModel(
|
|||||||
val gh = (current as? com.bitchat.android.geohash.ChannelID.Location)?.channel?.geohash
|
val gh = (current as? com.bitchat.android.geohash.ChannelID.Location)?.channel?.geohash
|
||||||
if (!gh.isNullOrEmpty()) {
|
if (!gh.isNullOrEmpty()) {
|
||||||
repo.setConversationGeohash(convKey, gh)
|
repo.setConversationGeohash(convKey, gh)
|
||||||
com.bitchat.android.nostr.GeohashConversationRegistry.set(convKey, gh)
|
GeohashConversationRegistry.set(convKey, gh)
|
||||||
}
|
}
|
||||||
onStartPrivateChat(convKey)
|
onStartPrivateChat(convKey)
|
||||||
Log.d(TAG, "🗨️ Started geohash DM with ${pubkeyHex} -> ${convKey} (geohash=${gh})")
|
Log.d(TAG, "🗨️ Started geohash DM with ${pubkeyHex} -> ${convKey} (geohash=${gh})")
|
||||||
@@ -260,7 +264,7 @@ class GeohashViewModel(
|
|||||||
handler = { event -> dmHandler.onGiftWrap(event, geohash, dmIdentity) }
|
handler = { event -> dmHandler.onGiftWrap(event, geohash, dmIdentity) }
|
||||||
)
|
)
|
||||||
// Also register alias in global registry for routing convenience
|
// Also register alias in global registry for routing convenience
|
||||||
com.bitchat.android.nostr.GeohashAliasRegistry.put("nostr_${dmIdentity.publicKeyHex.take(16)}", dmIdentity.publicKeyHex)
|
GeohashAliasRegistry.put("nostr_${dmIdentity.publicKeyHex.take(16)}", dmIdentity.publicKeyHex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
null -> {
|
null -> {
|
||||||
|
|||||||
@@ -298,6 +298,16 @@ class PrivateChatManager(
|
|||||||
if (!isPeerBlocked(senderPeerID)) {
|
if (!isPeerBlocked(senderPeerID)) {
|
||||||
// Ensure chat exists
|
// Ensure chat exists
|
||||||
messageManager.initializePrivateChat(senderPeerID)
|
messageManager.initializePrivateChat(senderPeerID)
|
||||||
|
|
||||||
|
// Exception: Nostr messages (nostr_ prefix) originate in Kotlin layer and MUST be added here.
|
||||||
|
if (senderPeerID.startsWith("nostr_")) {
|
||||||
|
if (suppressUnread) {
|
||||||
|
messageManager.addPrivateMessageNoUnread(senderPeerID, message)
|
||||||
|
} else {
|
||||||
|
messageManager.addPrivateMessage(senderPeerID, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Track as unread for read receipt purposes if not focused
|
// Track as unread for read receipt purposes if not focused
|
||||||
if (!suppressUnread && state.getSelectedPrivateChatPeerValue() != senderPeerID) {
|
if (!suppressUnread && state.getSelectedPrivateChatPeerValue() != senderPeerID) {
|
||||||
val unreadList = unreadReceivedMessages.getOrPut(senderPeerID) { mutableListOf() }
|
val unreadList = unreadReceivedMessages.getOrPut(senderPeerID) { mutableListOf() }
|
||||||
|
|||||||
Reference in New Issue
Block a user