Improve code organization and documentation (#325)

* Add MARK headers to improve code organization in major files

* Reorganize peer management functions in BluetoothMeshService

- Removed duplicate getCurrentPeerID(for:) function
- Consolidated peer identity functions in Peer Identity Mapping section
- Moved getPeerFingerprint(), getFingerprint(for:), isPeerIDOurs() to proper location
- Moved getCurrentPeerIDForFingerprint() and getCurrentPeerIDs() from Message Sending section
- Moved notifyPeerIDChange() to Peer Management section

* Consolidate peer management functions in BluetoothMeshService

- Moved getCachedPublicKey() and getCachedSigningKey() from Identity Cache Methods to Peer Connection Management
- Moved getPeerNicknames() and getPeerRSSI() to Peer Connection Management section
- Moved getAllConnectedPeerIDs(), notifyPeerListUpdate(), and cleanupStalePeers() to Peer Connection Management
- Removed duplicate function declarations after consolidation
- Improved code organization by grouping all peer-related functions together

* Consolidate message handling functions in ChatViewModel

- Moved handleHandshakeRequest() from floating location to Message Reception section
- Moved trimMessagesIfNeeded() and trimPrivateChatMessagesIfNeeded() to Message Batching section
- Improved code organization by grouping related message handling functions together
- Removed unnecessary comments from trim functions

* Improve ContentView organization with better documentation

- Added descriptive comments for complex inline computations
- Documented message extraction logic for private vs public chats
- Documented peer data computation and sorting logic
- Improved code readability by explaining complex operations inline
- Note: Attempted to extract complex computations into helper functions, but SwiftUI scope limitations made inline documentation a better approach

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
jack
2025-07-26 00:11:23 +02:00
committed by GitHub
co-authored by jack
parent 4568951736
commit 54c7eba8cb
4 changed files with 310 additions and 137 deletions
+98 -42
View File
@@ -16,10 +16,14 @@ import UIKit
#endif
class ChatViewModel: ObservableObject {
// MARK: - Published Properties
@Published var messages: [BitchatMessage] = []
private let maxMessages = 1337 // Maximum messages before oldest are removed
@Published var connectedPeers: [String] = []
// MARK: - Message Batching Properties
// Message batching for performance
private var pendingMessages: [BitchatMessage] = []
private var pendingPrivateMessages: [String: [BitchatMessage]] = [:] // peerID -> messages
@@ -44,6 +48,8 @@ class ChatViewModel: ObservableObject {
@Published var autocompleteRange: NSRange? = nil
@Published var selectedAutocompleteIndex: Int = 0
// MARK: - Autocomplete Properties
// Autocomplete optimization
private let mentionRegex = try? NSRegularExpression(pattern: "@([a-zA-Z0-9_]*)$", options: [])
private var cachedNicknames: [String] = []
@@ -52,18 +58,26 @@ class ChatViewModel: ObservableObject {
// Temporary property to fix compilation
@Published var showPasswordPrompt = false
// MARK: - Services and Storage
var meshService = BluetoothMeshService()
private let userDefaults = UserDefaults.standard
private let nicknameKey = "bitchat.nickname"
// MARK: - Caches
// Caches for expensive computations
private var rssiColorCache: [String: Color] = [:] // key: "\(rssi)_\(isDark)"
private var encryptionStatusCache: [String: EncryptionStatus] = [:] // key: peerID
// MARK: - Social Features
@Published var favoritePeers: Set<String> = [] // Now stores public key fingerprints instead of peer IDs
private var peerIDToPublicKeyFingerprint: [String: String] = [:] // Maps ephemeral peer IDs to persistent fingerprints
private var blockedUsers: Set<String> = [] // Stores public key fingerprints of blocked users
// MARK: - Encryption and Security
// Noise Protocol encryption status
@Published var peerEncryptionStatus: [String: EncryptionStatus] = [:] // peerID -> encryption status
@Published var verifiedFingerprints: Set<String> = [] // Set of verified fingerprints
@@ -71,12 +85,16 @@ class ChatViewModel: ObservableObject {
// Messages are naturally ephemeral - no persistent storage
// MARK: - Message Delivery Tracking
// Delivery tracking
private var deliveryTrackerCancellable: AnyCancellable?
// Track sent read receipts to avoid duplicates
private var sentReadReceipts: Set<String> = [] // messageID set
// MARK: - Initialization
init() {
loadNickname()
loadFavorites()
@@ -174,6 +192,8 @@ class ChatViewModel: ObservableObject {
#endif
}
// MARK: - Deinitialization
deinit {
// Clean up timer
messageBatchTimer?.invalidate()
@@ -182,6 +202,8 @@ class ChatViewModel: ObservableObject {
userDefaults.synchronize()
}
// MARK: - Nickname Management
private func loadNickname() {
if let savedNickname = userDefaults.string(forKey: nicknameKey) {
// Trim whitespace when loading
@@ -213,6 +235,8 @@ class ChatViewModel: ObservableObject {
saveNickname()
}
// MARK: - Favorites Management
private func loadFavorites() {
// Load favorites from secure storage
favoritePeers = SecureIdentityStateManager.shared.getFavorites()
@@ -223,6 +247,8 @@ class ChatViewModel: ObservableObject {
// This method is kept for compatibility
}
// MARK: - Blocked Users Management
private func loadBlockedUsers() {
// Load blocked users from secure storage
let allIdentities = SecureIdentityStateManager.shared.getAllSocialIdentities()
@@ -275,6 +301,8 @@ class ChatViewModel: ObservableObject {
return SecureIdentityStateManager.shared.isFavorite(fingerprint: fp)
}
// MARK: - Public Key and Identity Management
// Called when we receive a peer's public key
func registerPeerPublicKey(peerID: String, publicKeyData: Data) {
// Create a fingerprint from the public key (full SHA256, not truncated)
@@ -379,6 +407,8 @@ class ChatViewModel: ObservableObject {
}
}
// MARK: - Message Sending
func sendMessage(_ content: String) {
guard !content.isEmpty else { return }
@@ -479,6 +509,8 @@ class ChatViewModel: ObservableObject {
meshService.sendPrivateMessage(content, to: peerID, recipientNickname: recipientNickname, messageID: message.id)
}
// MARK: - Private Chat Management
func startPrivateChat(with peerID: String) {
let peerNickname = meshService.getPeerNicknames()[peerID] ?? "unknown"
@@ -581,6 +613,8 @@ class ChatViewModel: ObservableObject {
selectedPrivateChatFingerprint = nil
}
// MARK: - Message Retry Handling
@objc private func handleRetryMessage(_ notification: Notification) {
guard let messageID = notification.userInfo?["messageID"] as? String else { return }
@@ -605,6 +639,8 @@ class ChatViewModel: ObservableObject {
}
}
// MARK: - App Lifecycle
@objc private func appDidBecomeActive() {
// When app becomes active, send read receipts for visible private chat
if let peerID = selectedPrivateChatPeer {
@@ -798,6 +834,8 @@ class ChatViewModel: ObservableObject {
}
// MARK: - Emergency Functions
// PANIC: Emergency data clearing for activist safety
func panicClearAllData() {
// Flush any pending messages immediately before clearing
@@ -868,6 +906,8 @@ class ChatViewModel: ObservableObject {
// MARK: - Formatting Helpers
func formatTimestamp(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
@@ -909,6 +949,8 @@ class ChatViewModel: ObservableObject {
return color
}
// MARK: - Autocomplete
func updateAutocomplete(for text: String, cursorPosition: Int) {
// Quick early exit for empty text
guard cursorPosition > 0 else {
@@ -1001,6 +1043,8 @@ class ChatViewModel: ObservableObject {
return range.location + nickname.count + 2
}
// MARK: - Message Formatting
func getSenderColor(for message: BitchatMessage, colorScheme: ColorScheme) -> Color {
let isDark = colorScheme == .dark
let primaryColor = isDark ? Color.green : Color(red: 0, green: 0.5, blue: 0)
@@ -1390,7 +1434,8 @@ class ChatViewModel: ObservableObject {
rssiColorCache.removeAll()
}
// Trim messages to keep only the most recent maxMessages
// MARK: - Message Batching
private func trimMessagesIfNeeded() {
if messages.count > maxMessages {
let removeCount = messages.count - maxMessages
@@ -1398,7 +1443,6 @@ class ChatViewModel: ObservableObject {
}
}
// Trim private chat messages to keep only the most recent maxMessages
private func trimPrivateChatMessagesIfNeeded(for peerID: String) {
if let count = privateChats[peerID]?.count, count > maxMessages {
let removeCount = count - maxMessages
@@ -1406,8 +1450,6 @@ class ChatViewModel: ObservableObject {
}
}
// MARK: - Message Batching
private func addMessageToBatch(_ message: BitchatMessage) {
pendingMessages.append(message)
scheduleBatchFlush()
@@ -1512,6 +1554,8 @@ class ChatViewModel: ObservableObject {
}
}
// MARK: - Fingerprint Management
func showFingerprint(for peerID: String) {
showingFingerprintFor = peerID
}
@@ -1646,48 +1690,14 @@ class ChatViewModel: ObservableObject {
}
}
}
func handleHandshakeRequest(from peerID: String, nickname: String, pendingCount: UInt8) {
// Create a notification message
let notificationMessage = BitchatMessage(
sender: "system",
content: "📨 \(nickname) wants to send you \(pendingCount) message\(pendingCount == 1 ? "" : "s"). Open the conversation to receive.",
timestamp: Date(),
isRelay: false,
originalSender: nil,
isPrivate: false,
recipientNickname: nil,
senderPeerID: "system",
mentions: nil
)
// Add to messages
messages.append(notificationMessage)
trimMessagesIfNeeded()
// Show system notification
if let fingerprint = getFingerprint(for: peerID) {
let isFavorite = favoritePeers.contains(fingerprint)
if isFavorite {
// Send favorite notification
NotificationService.shared.sendPrivateMessageNotification(
from: nickname,
message: "\(pendingCount) message\(pendingCount == 1 ? "" : "s") pending",
peerID: peerID
)
} else {
// Send regular notification
NotificationService.shared.sendMentionNotification(
from: nickname,
message: "\(pendingCount) message\(pendingCount == 1 ? "" : "s") pending. Open conversation to receive."
)
}
}
}
}
// MARK: - BitchatDelegate
extension ChatViewModel: BitchatDelegate {
// MARK: - Command Handling
private func handleCommand(_ command: String) {
let parts = command.split(separator: " ")
guard let cmd = parts.first else { return }
@@ -2042,6 +2052,46 @@ extension ChatViewModel: BitchatDelegate {
}
}
// MARK: - Message Reception
func handleHandshakeRequest(from peerID: String, nickname: String, pendingCount: UInt8) {
// Create a notification message
let notificationMessage = BitchatMessage(
sender: "system",
content: "📨 \(nickname) wants to send you \(pendingCount) message\(pendingCount == 1 ? "" : "s"). Open the conversation to receive.",
timestamp: Date(),
isRelay: false,
originalSender: nil,
isPrivate: false,
recipientNickname: nil,
senderPeerID: "system",
mentions: nil
)
// Add to messages
messages.append(notificationMessage)
trimMessagesIfNeeded()
// Show system notification
if let fingerprint = getFingerprint(for: peerID) {
let isFavorite = favoritePeers.contains(fingerprint)
if isFavorite {
// Send favorite notification
NotificationService.shared.sendPrivateMessageNotification(
from: nickname,
message: "\(pendingCount) message\(pendingCount == 1 ? "" : "s") pending",
peerID: peerID
)
} else {
// Send regular notification
NotificationService.shared.sendMentionNotification(
from: nickname,
message: "\(pendingCount) message\(pendingCount == 1 ? "" : "s") pending. Open conversation to receive."
)
}
}
}
func didReceiveMessage(_ message: BitchatMessage) {
@@ -2342,6 +2392,8 @@ extension ChatViewModel: BitchatDelegate {
#endif
}
// MARK: - Peer Connection Events
func didConnectToPeer(_ peerID: String) {
isConnected = true
@@ -2435,6 +2487,8 @@ extension ChatViewModel: BitchatDelegate {
}
}
// MARK: - Helper Methods
private func parseMentions(from content: String) -> [String] {
let pattern = "@([a-zA-Z0-9_]+)"
let regex = try? NSRegularExpression(pattern: pattern, options: [])
@@ -2461,6 +2515,8 @@ extension ChatViewModel: BitchatDelegate {
return SecureIdentityStateManager.shared.isFavorite(fingerprint: fingerprint)
}
// MARK: - Delivery Tracking
func didReceiveDeliveryAck(_ ack: DeliveryAck) {
// Find the message and update its delivery status
updateMessageDeliveryStatus(ack.originalMessageID, status: .delivered(to: ack.recipientNickname, at: ack.timestamp))