mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 18:05:19 +00:00
Refactor/robustness (#446)
* Refactor BitChat for improved robustness and performance Major refactoring to simplify architecture and fix critical issues: Architecture Improvements: - Replace complex BluetoothMeshService with simplified SimplifiedBluetoothService - Consolidate message routing and peer management into unified services - Remove redundant caching layers and optimize performance Bug Fixes: - Fix critical BLE peer mapping corruption in mesh networks - Fix encrypted message routing failures in multi-peer scenarios - Fix app freezes and Main Thread Checker warnings - Fix BLE message delivery in dual-role connections - Fix favorite toggle UI not updating instantly - Fix Nostr offline messaging with 24-hour message filtering Features: - Add command processor for chat commands - Add autocomplete service for mentions and commands - Improve private chat management with dedicated service - Add unified peer service for consistent state management Performance: - Optimize BLE reconnection speed - Reduce excessive logging throughout codebase - Improve message deduplication efficiency - Optimize UI updates and state management * Improvements refactor robust (#441) * remove unused code * remove more * TLV for announcement * restore * restore * restore? * messages tlv too (#442) * Fix Nostr notification and read receipt issues, add TLV encoding, cleanup unused code ## Notification & Read Receipt Fixes - Fixed toolbar notification icon appearing incorrectly on app restart for already-read messages - Fixed read receipts being incorrectly deleted on startup when privateChats was empty - Fixed messages not being marked as read when opening chat for first time - Fixed senderPeerID not being updated during message consolidation - Added startup phase logic to block old messages (>30s) while allowing recent ones - Fixed unread status checking across all storage locations (ephemeral, stable Noise keys, temporary Nostr IDs) ## TLV Encoding Implementation - Implemented Type-Length-Value encoding for private message payloads - Added PrivateMessagePacket struct with TLV encode/decode methods - Enhanced message structure for better extensibility and robustness ## Code Cleanup - Removed unused PeerStateManager class and related dependencies - Removed dead protocol types (DeliveryAck, ProtocolAck/Nack, NoiseIdentityAnnouncement) - Cleaned up BitchatDelegate by removing unused methods - Removed excessive debug logging throughout ChatViewModel - Added test-only ProtocolNack helper for integration tests ## Technical Details - Messages stored under three ID types: ephemeral peer IDs, stable Noise key hexes, temporary Nostr IDs - Fixed cleanupOldReadReceipts() to skip when privateChats is empty or during startup - Updated message consolidation to properly update senderPeerID - Restored NIP-17 timestamp randomization (±15 minutes) for privacy --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
This commit is contained in:
@@ -98,10 +98,10 @@ class PeerManager: ObservableObject {
|
||||
@Published var favorites: [BitchatPeer] = []
|
||||
@Published var mutualFavorites: [BitchatPeer] = []
|
||||
|
||||
private let meshService: BluetoothMeshService
|
||||
private let meshService: SimplifiedBluetoothService
|
||||
private let favoritesService = FavoritesPersistenceService.shared
|
||||
|
||||
init(meshService: BluetoothMeshService) {
|
||||
init(meshService: SimplifiedBluetoothService) {
|
||||
self.meshService = meshService
|
||||
updatePeers()
|
||||
|
||||
@@ -142,8 +142,6 @@ class PeerManager: ObservableObject {
|
||||
|
||||
// Safety check: Never add our own peer ID
|
||||
if peerID == meshService.myPeerID {
|
||||
SecureLogger.log("⚠️ Skipping self peer ID \(peerID) in peer list",
|
||||
category: SecureLogger.session, level: .warning)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -195,22 +193,19 @@ class PeerManager: ObservableObject {
|
||||
|
||||
// Skip if this peer is already connected (by nickname)
|
||||
if connectedNicknames.contains(favorite.peerNickname) {
|
||||
SecureLogger.log(" - Skipping '\(favorite.peerNickname)' (key: \(favoriteKey.hexEncodedString())) - already connected",
|
||||
category: SecureLogger.session, level: .debug)
|
||||
// Skipping favorite - already connected
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip if we already added a peer with this ID (prevents duplicates)
|
||||
if addedPeerIDs.contains(favoriteID) {
|
||||
SecureLogger.log(" - Skipping '\(favorite.peerNickname)' - peer ID already added",
|
||||
category: SecureLogger.session, level: .debug)
|
||||
// Skipping favorite - peer ID already added
|
||||
continue
|
||||
}
|
||||
|
||||
// Only add peers that WE favorite (not just ones who favorite us)
|
||||
if !favorite.isFavorite {
|
||||
SecureLogger.log(" - Skipping '\(favorite.peerNickname)' - we don't favorite them (they favorite us: \(favorite.theyFavoritedUs))",
|
||||
category: SecureLogger.session, level: .debug)
|
||||
// Skipping - we don't favorite them
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -292,21 +287,7 @@ class PeerManager: ObservableObject {
|
||||
SecureLogger.log("📊 Peer list update: \(allPeers.count) total (\(connectedCount) connected, \(offlineCount) offline), \(favorites.count) favorites, \(mutualFavorites.count) mutual",
|
||||
category: SecureLogger.session, level: .info)
|
||||
|
||||
// Log each peer's status
|
||||
for peer in allPeers {
|
||||
let statusIcon: String
|
||||
switch peer.connectionState {
|
||||
case .bluetoothConnected:
|
||||
statusIcon = "🟢"
|
||||
case .nostrAvailable:
|
||||
statusIcon = "🌐"
|
||||
case .offline:
|
||||
statusIcon = "🔴"
|
||||
}
|
||||
let favoriteIcon = peer.isMutualFavorite ? "💕" : (peer.isFavorite ? "⭐" : (peer.theyFavoritedUs ? "🌙" : ""))
|
||||
SecureLogger.log(" \(statusIcon) \(peer.displayName) (ID: \(peer.id.prefix(8))...) \(favoriteIcon)",
|
||||
category: SecureLogger.session, level: .debug)
|
||||
}
|
||||
// Detailed peer status logging removed for brevity
|
||||
} else if previousCount != allPeers.count {
|
||||
// Only log non-favorite updates if count changed
|
||||
SecureLogger.log("✅ Updated peer list: \(allPeers.count) total peers",
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
/// Unified model for tracking all peer session data
|
||||
/// Consolidates multiple redundant data structures into a single source of truth
|
||||
class PeerSession {
|
||||
// Core identification
|
||||
let peerID: String
|
||||
var nickname: String
|
||||
|
||||
// Bluetooth connection
|
||||
var peripheral: CBPeripheral?
|
||||
var peripheralID: String?
|
||||
var characteristic: CBCharacteristic?
|
||||
|
||||
// Authentication and encryption
|
||||
var isAuthenticated: Bool = false
|
||||
var hasEstablishedNoiseSession: Bool = false
|
||||
var fingerprint: String?
|
||||
|
||||
// Connection state
|
||||
var isConnected: Bool = false
|
||||
var lastSeen: Date
|
||||
|
||||
// Protocol state
|
||||
var hasAnnounced: Bool = false
|
||||
var hasReceivedAnnounce: Bool = false
|
||||
var isActivePeer: Bool = false
|
||||
|
||||
// Message tracking
|
||||
var lastMessageSent: Date?
|
||||
var lastMessageReceived: Date?
|
||||
var pendingMessages: [String] = []
|
||||
|
||||
// Connection timing
|
||||
var lastConnectionTime: Date?
|
||||
var lastSuccessfulMessageTime: Date?
|
||||
var lastHeardFromPeer: Date?
|
||||
|
||||
// Availability tracking
|
||||
var isAvailable: Bool = false
|
||||
|
||||
// Identity binding
|
||||
var identityBinding: PeerIdentityBinding?
|
||||
|
||||
init(peerID: String, nickname: String = "Unknown") {
|
||||
self.peerID = peerID
|
||||
self.nickname = nickname
|
||||
self.lastSeen = Date()
|
||||
}
|
||||
|
||||
/// Update Bluetooth connection info
|
||||
func updateBluetoothConnection(peripheral: CBPeripheral?, characteristic: CBCharacteristic?) {
|
||||
self.peripheral = peripheral
|
||||
self.peripheralID = peripheral?.identifier.uuidString
|
||||
self.characteristic = characteristic
|
||||
self.isConnected = (peripheral?.state == .connected)
|
||||
if isConnected {
|
||||
self.lastSeen = Date()
|
||||
}
|
||||
}
|
||||
|
||||
/// Update authentication state
|
||||
func updateAuthenticationState(authenticated: Bool, noiseSession: Bool) {
|
||||
self.isAuthenticated = authenticated
|
||||
self.hasEstablishedNoiseSession = noiseSession
|
||||
if authenticated {
|
||||
self.isActivePeer = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Check if session is stale
|
||||
var isStale: Bool {
|
||||
// Consider stale if not seen for more than 5 minutes and not connected
|
||||
return !isConnected && Date().timeIntervalSince(lastSeen) > 300
|
||||
}
|
||||
|
||||
/// Get display status for UI
|
||||
var displayStatus: String {
|
||||
if isConnected {
|
||||
if isAuthenticated {
|
||||
return "🟢" // Connected and authenticated
|
||||
} else {
|
||||
return "🟡" // Connected but not authenticated
|
||||
}
|
||||
} else {
|
||||
return "🔴" // Not connected
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user