Show Bluetooth permission alerts on launch and foreground (#765)

* Fix test suite peer ID collisions

Use unique peer IDs for each test suite to prevent global registry
collisions when Swift Testing runs suites in parallel.

- PrivateChatE2ETests: PRIV_* prefix
- PublicChatE2ETests: PUB_* prefix
- Update all peer ID references to use actual instance IDs

This fixes the race condition where simplePublicMessage() was receiving
duplicate deliveries due to registry contamination.

* Add Bluetooth permission & state alerts on launch and foreground

Wire up existing Bluetooth alert infrastructure to show notifications
when Bluetooth is off, unauthorized, or unsupported.

Changes:
- Add didUpdateBluetoothState() to BitchatDelegate protocol
- BLEService now notifies delegate when Bluetooth state changes
- ChatViewModel implements delegate method to show alerts
- Check Bluetooth state on app launch (after 100ms delay)
- Check Bluetooth state when app comes to foreground
- Add getCurrentBluetoothState() method to BLEService

The UI alert already existed but wasn't wired up. Now users will see
appropriate alerts for:
- Bluetooth turned off
- Bluetooth permission denied
- Bluetooth unsupported on device

Alert includes a button to open Settings on iOS.

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
jack
2025-10-06 22:47:11 +02:00
committed by GitHub
co-authored by jack
parent af6703cf24
commit b5b05977fb
5 changed files with 46 additions and 5 deletions
+6 -2
View File
@@ -59,6 +59,7 @@
///
import Foundation
import CoreBluetooth
// MARK: - Message Types
@@ -164,14 +165,17 @@ protocol BitchatDelegate: AnyObject {
func didConnectToPeer(_ peerID: String)
func didDisconnectFromPeer(_ peerID: String)
func didUpdatePeerList(_ peers: [String])
// Optional method to check if a fingerprint belongs to a favorite peer
func isFavorite(fingerprint: String) -> Bool
func didUpdateMessageDeliveryStatus(_ messageID: String, status: DeliveryStatus)
// Low-level events for better separation of concerns
func didReceiveNoisePayload(from peerID: String, type: NoisePayloadType, payload: Data, timestamp: Date)
// Bluetooth state updates for user notifications
func didUpdateBluetoothState(_ state: CBManagerState)
func didReceivePublicMessage(from peerID: String, nickname: String, content: String, timestamp: Date)
}
+10 -1
View File
@@ -552,7 +552,11 @@ final class BLEService: NSObject {
func getNoiseService() -> NoiseEncryptionService {
return noiseService
}
func getCurrentBluetoothState() -> CBManagerState {
return centralManager?.state ?? .unknown
}
// MARK: Messaging
// Transport protocol conformance helper: simplified public message send
@@ -694,6 +698,11 @@ extension BLEService: GossipSyncManager.Delegate {
extension BLEService: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
// Notify delegate about state change on main thread
Task { @MainActor in
self.delegate?.didUpdateBluetoothState(central.state)
}
if central.state == .poweredOn {
// Start scanning - use allow duplicates for faster discovery when active
startScanning()
+25 -2
View File
@@ -549,7 +549,16 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
// Start mesh service immediately
meshService.startServices()
// Check initial Bluetooth state after a brief delay to allow centralManager initialization
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
guard let self = self else { return }
if let bleService = self.meshService as? BLEService {
let state = bleService.getCurrentBluetoothState()
self.updateBluetoothState(state)
}
}
// Announce Tor status (geohash-only; do not show in mesh chat). Only when auto-start is allowed.
if TorManager.shared.torEnforced && !torStatusAnnounced && TorManager.shared.isAutoStartAllowed() {
torStatusAnnounced = true
@@ -2834,6 +2843,12 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
@MainActor
@objc private func appDidBecomeActive() {
// Check Bluetooth state and show alert if needed
if let bleService = meshService as? BLEService {
let currentState = bleService.getCurrentBluetoothState()
updateBluetoothState(currentState)
}
// When app becomes active, send read receipts for visible private chat
if let peerID = selectedPrivateChatPeer {
// Try immediately
@@ -4597,8 +4612,16 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
}
// Mention parsing moved from BLE use the existing non-optional helper below
// MARK: - Bluetooth State Monitoring
func didUpdateBluetoothState(_ state: CBManagerState) {
Task { @MainActor in
updateBluetoothState(state)
}
}
// MARK: - Peer Connection Events
func didConnectToPeer(_ peerID: String) {
SecureLogger.debug("🤝 Peer connected: \(peerID)", category: .session)