mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 10:45:20 +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:
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// MockSimplifiedBluetoothService.swift
|
||||
// bitchatTests
|
||||
//
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// For more information, see <https://unlicense.org>
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
@testable import bitchat
|
||||
|
||||
// Mock implementation that mimics SimplifiedBluetoothService behavior
|
||||
class MockSimplifiedBluetoothService: NSObject {
|
||||
|
||||
// MARK: - Properties matching SimplifiedBluetoothService
|
||||
|
||||
weak var delegate: BitchatDelegate?
|
||||
var myPeerID: String = "MOCK1234"
|
||||
var myNickname: String = "MockUser"
|
||||
|
||||
// Test-specific properties
|
||||
var sentMessages: [(message: BitchatMessage, packet: BitchatPacket)] = []
|
||||
var sentPackets: [BitchatPacket] = []
|
||||
var connectedPeers: Set<String> = []
|
||||
var messageDeliveryHandler: ((BitchatMessage) -> Void)?
|
||||
var packetDeliveryHandler: ((BitchatPacket) -> Void)?
|
||||
|
||||
// Compatibility properties for old tests
|
||||
var mockNickname: String {
|
||||
get { return myNickname }
|
||||
set { myNickname = newValue }
|
||||
}
|
||||
|
||||
var nickname: String {
|
||||
return myNickname
|
||||
}
|
||||
|
||||
var peerID: String {
|
||||
return myPeerID
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Methods matching SimplifiedBluetoothService
|
||||
|
||||
func setNickname(_ nickname: String) {
|
||||
self.myNickname = nickname
|
||||
}
|
||||
|
||||
func startServices() {
|
||||
// Mock implementation - do nothing
|
||||
}
|
||||
|
||||
func stopServices() {
|
||||
// Mock implementation - do nothing
|
||||
}
|
||||
|
||||
func isPeerConnected(_ peerID: String) -> Bool {
|
||||
return connectedPeers.contains(peerID)
|
||||
}
|
||||
|
||||
func getPeerNicknames() -> [String: String] {
|
||||
var nicknames: [String: String] = [:]
|
||||
for peer in connectedPeers {
|
||||
nicknames[peer] = "MockPeer_\(peer)"
|
||||
}
|
||||
return nicknames
|
||||
}
|
||||
|
||||
func getPeers() -> [String: String] {
|
||||
return getPeerNicknames()
|
||||
}
|
||||
|
||||
func sendMessage(_ content: String, mentions: [String] = [], to recipientID: String? = nil, messageID: String? = nil, timestamp: Date? = nil) {
|
||||
let message = BitchatMessage(
|
||||
id: messageID ?? UUID().uuidString,
|
||||
sender: myNickname,
|
||||
content: content,
|
||||
timestamp: timestamp ?? Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: recipientID != nil,
|
||||
recipientNickname: nil,
|
||||
senderPeerID: myPeerID,
|
||||
mentions: mentions.isEmpty ? nil : mentions
|
||||
)
|
||||
|
||||
if let payload = message.toBinaryPayload() {
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: myPeerID.data(using: .utf8)!,
|
||||
recipientID: recipientID?.data(using: .utf8),
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
sentMessages.append((message, packet))
|
||||
sentPackets.append(packet)
|
||||
|
||||
// Simulate local echo
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
// Call delivery handler if set
|
||||
messageDeliveryHandler?(message)
|
||||
}
|
||||
}
|
||||
|
||||
func sendPrivateMessage(_ content: String, to recipientPeerID: String, recipientNickname: String, messageID: String) {
|
||||
let message = BitchatMessage(
|
||||
id: messageID,
|
||||
sender: myNickname,
|
||||
content: content,
|
||||
timestamp: Date(),
|
||||
isRelay: false,
|
||||
originalSender: nil,
|
||||
isPrivate: true,
|
||||
recipientNickname: recipientNickname,
|
||||
senderPeerID: myPeerID,
|
||||
mentions: nil
|
||||
)
|
||||
|
||||
if let payload = message.toBinaryPayload() {
|
||||
let packet = BitchatPacket(
|
||||
type: 0x01,
|
||||
senderID: myPeerID.data(using: .utf8)!,
|
||||
recipientID: recipientPeerID.data(using: .utf8)!,
|
||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||
payload: payload,
|
||||
signature: nil,
|
||||
ttl: 3
|
||||
)
|
||||
|
||||
sentMessages.append((message, packet))
|
||||
sentPackets.append(packet)
|
||||
|
||||
// Simulate local echo
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
// Call delivery handler if set
|
||||
messageDeliveryHandler?(message)
|
||||
}
|
||||
}
|
||||
|
||||
func sendFavoriteNotification(to peerID: String, isFavorite: Bool) {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func sendReadReceipt(_ receipt: ReadReceipt, to peerID: String) {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func sendBroadcastAnnounce() {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func getPeerFingerprint(_ peerID: String) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getNoiseSessionState(for peerID: String) -> LazyHandshakeState {
|
||||
return .none
|
||||
}
|
||||
|
||||
func triggerHandshake(with peerID: String) {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
func emergencyDisconnectAll() {
|
||||
connectedPeers.removeAll()
|
||||
delegate?.didUpdatePeerList([])
|
||||
}
|
||||
|
||||
func getNoiseService() -> NoiseEncryptionService {
|
||||
return NoiseEncryptionService()
|
||||
}
|
||||
|
||||
func getFingerprint(for peerID: String) -> String? {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - Test Helper Methods
|
||||
|
||||
func simulateConnectedPeer(_ peerID: String) {
|
||||
connectedPeers.insert(peerID)
|
||||
delegate?.didConnectToPeer(peerID)
|
||||
delegate?.didUpdatePeerList(Array(connectedPeers))
|
||||
}
|
||||
|
||||
func simulateDisconnectedPeer(_ peerID: String) {
|
||||
connectedPeers.remove(peerID)
|
||||
delegate?.didDisconnectFromPeer(peerID)
|
||||
delegate?.didUpdatePeerList(Array(connectedPeers))
|
||||
}
|
||||
|
||||
func simulateIncomingMessage(_ message: BitchatMessage) {
|
||||
delegate?.didReceiveMessage(message)
|
||||
}
|
||||
|
||||
func simulateIncomingPacket(_ packet: BitchatPacket) {
|
||||
// Process through the actual handling logic
|
||||
if let message = BitchatMessage.fromBinaryPayload(packet.payload) {
|
||||
delegate?.didReceiveMessage(message)
|
||||
}
|
||||
packetDeliveryHandler?(packet)
|
||||
}
|
||||
|
||||
func getConnectedPeers() -> [String] {
|
||||
return Array(connectedPeers)
|
||||
}
|
||||
|
||||
// MARK: - Compatibility methods for old tests
|
||||
|
||||
func sendPrivateMessage(_ content: String, to recipientPeerID: String, recipientNickname: String, messageID: String? = nil) {
|
||||
sendPrivateMessage(content, to: recipientPeerID, recipientNickname: recipientNickname, messageID: messageID ?? UUID().uuidString)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user