Replace blocking Thread.sleep in stopServices() with a cooperative
RunLoop-based delay. This allows BLE callbacks to fire during the
shutdown delay, improving reliability of leave message transmission.
The delay is needed to give the leave message time to transmit before
disconnecting peers and stopping the BLE stack.
Refactor participant tracking logic into a dedicated, testable class:
- Create GeohashParticipantTracker with GeohashParticipantContext protocol
- Move GeoPerson struct to new file
- Extract participant recording, refresh, and count logic
- Add 18 comprehensive tests for the tracker
- Update ChatViewModel to use tracker via dependency injection
- Subscribe to tracker's objectWillChange for SwiftUI observation
This reduces coupling in ChatViewModel and makes participant tracking
testable in isolation.
The original tests using in-place Data modification with `data[0] ^= 0xFF`
caused signal 5 crashes during parallel test execution. Fixed by:
- Using `import struct Foundation.Data` for efficiency
- Converting Data to byte array before modification to avoid the crash
- Simplified error-throwing tests to use boolean flags instead of
complex error type matching
Introduce CommandContextProvider protocol to define what CommandProcessor
needs from its context. This follows the Dependency Inversion Principle:
- Create CommandContextProvider protocol with 15 methods/properties
- Create CommandGeoParticipant struct for geo participant data
- Add getVisibleGeoParticipants() to ChatViewModel
- ChatViewModel now conforms to CommandContextProvider
- CommandProcessor depends on protocol, not concrete ChatViewModel
Benefits:
- Breaks the tight coupling between CommandProcessor and ChatViewModel
- Makes CommandProcessor more testable (can use mock context)
- Clear contract for what CommandProcessor needs
- Backwards-compatible via chatViewModel property alias
Also fixes a concurrency bug in BitchatApp where notification delegate
was accessing main-actor-isolated property from arbitrary thread.
precondition() crashes the app in release builds if violated. This is
dangerous for cryptographic code that may receive malformed input from
network data or key derivation bugs.
Changed to guard statements that throw typed errors instead:
- XChaCha20Poly1305Compat.Error.invalidKeyLength
- XChaCha20Poly1305Compat.Error.invalidNonceLength
This allows callers to handle errors gracefully rather than crashing.
Add proper error logging for Bech32 decode failures instead of silently
returning. This improves debuggability by making it clear when and why
npub decoding fails for favorite notifications, delivery acks, and read
receipts.
The previous empty catch blocks made it difficult to diagnose issues
with malformed or invalid npub addresses. Now all decoding errors are
properly logged with context about which operation failed.
* Add localizable strings in show commands view n refactor code to remove duplicate string declarations.
* Modify struct to enum in separate file to Models/CommandsInfo.
* Add corrections code in refactory and enum.
* Correct code in command enum and ContentView.
* Dedicated CommandSuggestionsView to extract code
* Simplify CommandInfo + minor optimization
* Fix preview
---------
Co-authored-by: islam <2553451+qalandarov@users.noreply.github.com>
* Fix verification persistence on iOS when app backgrounds (#785)
Verification status was not being saved when the iOS app entered background
state, causing verified contacts to lose their verification status after the
app was closed. This happened because iOS can terminate backgrounded apps
without calling applicationWillTerminate.
Changes:
- Add saveIdentityState() method to force-save identity data without stopping services
- Call saveIdentityState() when iOS app enters background state
- Refactor applicationWillTerminate() to use new method
- Fix selector name typo (appWillTerminate -> applicationWillTerminate)
The verification data is now properly persisted to encrypted keychain storage
whenever the app backgrounds, ensuring verification status persists across
app launches.
Fixes#785
* Save identity state during verification events
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
* Harden background BLE restoration and notifications
* Guard BLE restoration paths to iOS
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
* Simplify validation, compression heuristics, and notification scheduling
* Consolidate notification logic and add InputValidator monitoring
Follow-up improvements to address PR feedback:
1. Consolidate notification functions
- Add interruptionLevel parameter to sendLocalNotification
- Refactor sendNetworkAvailableNotification to use consolidated function
- Removes 12 lines of duplicate code
2. Add monitoring to InputValidator
- Log control character rejections for production monitoring
- Privacy-preserving: logs length + count, not actual content
- Uses .security category for proper log routing
3. Add comprehensive InputValidator tests
- 28 test cases covering validation, control characters, unicode, edge cases
- Ensures behavioral changes are well-tested and documented
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
* Improve mesh media throughput
* Reserve media slots atomically
* Prioritize small fragment trains
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
* PeerID 28/n: `ChatViewModel.getShortIDForNoiseKey`
* PeerID 29/n: `BLEService` + remove dupe funcs from #823
* `handleFileTransfer` to use PeerID
* `sendMessage` and `sendPrivateMessage`
* PeerID 30/n: Update some leftovers
* `lowercased()` inside PeerID for normalization
* PeerID 31/n: Remove String interop to be explicit
* MockBLEService: Remove direct target delivery
This causes a delivery even if the sender and receiver are not connected
---------
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Implements mutually-exclusive image picker presentations to fix the error
"Currently, only presenting a single sheet is supported" when tapping
camera in a DM.
Solution:
- ContentView: Only presents image picker when NOT in a sheet
(when showSidebar=false and no private chat active)
- peopleSheetView: Only presents image picker when IN a sheet
(when showSidebar=true or private chat active)
This ensures only ONE presentation is active at any time, preventing
conflicts. Uses fullScreenCover on iOS to allow presentation over sheets.
Addresses feedback from @qalandarov in PR #834 with minimal approach.
Co-authored-by: jack <jackjackbits@users.noreply.github.com>