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 40f9274e..a8af7baf 100644 --- a/app/src/main/java/com/bitchat/android/nostr/NostrGeohashService.kt +++ b/app/src/main/java/com/bitchat/android/nostr/NostrGeohashService.kt @@ -135,6 +135,76 @@ class NostrGeohashService( Log.i(TAG, "✅ Nostr integration initialized with gift wrap subscription") } } + + /** + * Panic/reset flow for Nostr + geohash systems. + * - Disconnect and clear all Nostr relay subscriptions and caches + * - Delete device-wide Nostr keys and per-geohash seed cache + * - Clear geohash participants, nicknames, message history + * - Recreate identity and reconnect; reinitialize subscriptions from scratch + */ + fun panicResetNostrAndGeohash() { + coroutineScope.launch { + try { + val relayManager = NostrRelayManager.getInstance(application) + + // 1) Disconnect from all relays + relayManager.disconnect() + + // 2) Clear all subscription tracking and caches + relayManager.clearAllSubscriptions() + + // 3) Clear Nostr identity (npub/private) and geohash identity cache/seed + try { + NostrIdentityBridge.clearAllAssociations(application) + } catch (e: Exception) { + Log.e(TAG, "Failed to clear Nostr associations: ${e.message}") + } + + // 4) Clear local geohash state (participants, nicknames, message history) + try { + geohashParticipants.clear() + geoNicknames.clear() + geohashMessageHistory.clear() + processedNostrEvents.clear() + processedNostrEventOrder.clear() + currentGeohashSubscriptionId = null + currentGeohashDmSubscriptionId = null + currentGeohash = null + state.setGeohashPeople(emptyList()) + state.setTeleportedGeo(emptySet()) + state.setGeohashParticipantCounts(emptyMap()) + // Stop any timers/jobs + geohashSamplingJob?.cancel() + geohashSamplingJob = null + geoParticipantsTimer?.cancel() + geoParticipantsTimer = null + } catch (e: Exception) { + Log.e(TAG, "Failed to clear geohash state: ${e.message}") + } + + // 5) Recreate identity and reconnect, then re-subscribe + try { + // Touch identity to recreate + val identity = NostrIdentityBridge.getCurrentNostrIdentity(application) + if (identity == null) { + Log.w(TAG, "No identity after reset; skipping subscriptions") + return@launch + } + + // Reconnect to relays and reinitialize subscriptions + relayManager.connect() + initializeNostrIntegration() + // Re-establish location channel state observers and subscriptions + initializeLocationChannelState() + } catch (e: Exception) { + Log.e(TAG, "Failed to reinitialize Nostr after reset: ${e.message}") + } + } catch (e: Exception) { + Log.e(TAG, "panicResetNostrAndGeohash error: ${e.message}") + } + } + } /** * Initialize location channel state diff --git a/app/src/main/java/com/bitchat/android/nostr/NostrRelayManager.kt b/app/src/main/java/com/bitchat/android/nostr/NostrRelayManager.kt index 9572b010..92f4afe8 100644 --- a/app/src/main/java/com/bitchat/android/nostr/NostrRelayManager.kt +++ b/app/src/main/java/com/bitchat/android/nostr/NostrRelayManager.kt @@ -456,6 +456,31 @@ class NostrRelayManager private constructor() { } } + /** + * Clear all subscription tracking, message handlers, routing caches, and queued messages. + * Intended for panic/reset flows prior to reconnecting and re-subscribing from scratch. + */ + fun clearAllSubscriptions() { + try { + // Clear persistent subscription tracking + activeSubscriptions.clear() + messageHandlers.clear() + subscriptions.clear() + + // Clear routing caches (per-geohash relay selections) + geohashToRelays.clear() + + // Clear any queued messages waiting to be sent + synchronized(messageQueueLock) { + messageQueue.clear() + } + + Log.i(TAG, "🧹 Cleared all Nostr subscriptions and routing caches") + } catch (e: Exception) { + Log.e(TAG, "Failed to clear subscriptions: ${e.message}") + } + } + /** * Get detailed status for all relays */ 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 9fdbc19a..0a0ea6a7 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt @@ -556,8 +556,12 @@ class ChatViewModel( // Clear all notifications notificationManager.clearAllNotifications() - // Clear geohash message history - nostrGeohashService.clearGeohashMessageHistory() + // Clear Nostr/geohash state, keys, connections, and reinitialize from scratch + try { + nostrGeohashService.panicResetNostrAndGeohash() + } catch (e: Exception) { + Log.e(TAG, "Failed to reset Nostr/geohash: ${e.message}") + } // Reset nickname val newNickname = "anon${Random.nextInt(1000, 9999)}"