Add Bluetooth status alerts (#385)

- Add alert UI to notify users when Bluetooth is off/unauthorized
- Monitor Bluetooth state changes in BluetoothMeshService
- Show appropriate messages for different Bluetooth states
- Add Settings button to open system settings on iOS
- Check initial Bluetooth state on app startup

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
jack
2025-08-01 21:51:32 +02:00
committed by GitHub
co-authored by jack
parent 8ec39f566d
commit b4b22e0e05
3 changed files with 74 additions and 0 deletions
@@ -1937,6 +1937,15 @@ class BluetoothMeshService: NSObject {
cleanupStaleSessions()
cleanupUnknownPeers()
// Report initial Bluetooth state to delegate
if let chatViewModel = delegate as? ChatViewModel {
// Use the central manager state as primary indicator
let currentState = centralManager?.state ?? .unknown
Task { @MainActor in
chatViewModel.updateBluetoothState(currentState)
}
}
// Starting services
// Start both central and peripheral services
if centralManager?.state == .poweredOn {
@@ -4988,6 +4997,13 @@ extension BluetoothMeshService: CBCentralManagerDelegate {
@unknown default: break
}
// Notify ChatViewModel of Bluetooth state change
if let chatViewModel = delegate as? ChatViewModel {
Task { @MainActor in
chatViewModel.updateBluetoothState(central.state)
}
}
if central.state == .unsupported {
} else if central.state == .unauthorized {
} else if central.state == .poweredOff {
@@ -5540,6 +5556,13 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
@unknown default: break
}
// Notify ChatViewModel of Bluetooth state change
if let chatViewModel = delegate as? ChatViewModel {
Task { @MainActor in
chatViewModel.updateBluetoothState(peripheral.state)
}
}
switch peripheral.state {
case .unsupported:
break
+36
View File
@@ -82,6 +82,7 @@ import SwiftUI
import Combine
import CryptoKit
import CommonCrypto
import CoreBluetooth
#if os(iOS)
import UIKit
#endif
@@ -155,6 +156,11 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
@Published var verifiedFingerprints: Set<String> = [] // Set of verified fingerprints
@Published var showingFingerprintFor: String? = nil // Currently showing fingerprint sheet for peer
// Bluetooth state management
@Published var showBluetoothAlert = false
@Published var bluetoothAlertMessage = ""
@Published var bluetoothState: CBManagerState = .unknown
// Messages are naturally ephemeral - no persistent storage
// MARK: - Message Delivery Tracking
@@ -819,6 +825,36 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
}
}
// MARK: - Bluetooth State Management
/// Updates the Bluetooth state and shows appropriate alerts
/// - Parameter state: The current Bluetooth manager state
@MainActor
func updateBluetoothState(_ state: CBManagerState) {
bluetoothState = state
switch state {
case .poweredOff:
bluetoothAlertMessage = "Bluetooth is turned off. Please turn on Bluetooth in Settings to use BitChat."
showBluetoothAlert = true
case .unauthorized:
bluetoothAlertMessage = "BitChat needs Bluetooth permission to connect with nearby devices. Please enable Bluetooth access in Settings."
showBluetoothAlert = true
case .unsupported:
bluetoothAlertMessage = "This device does not support Bluetooth. BitChat requires Bluetooth to function."
showBluetoothAlert = true
case .poweredOn:
// Hide alert when Bluetooth is powered on
showBluetoothAlert = false
bluetoothAlertMessage = ""
case .unknown, .resetting:
// Don't show alerts for transient states
showBluetoothAlert = false
@unknown default:
showBluetoothAlert = false
}
}
// MARK: - Private Chat Management
/// Initiates a private chat session with a peer.
+15
View File
@@ -7,6 +7,9 @@
//
import SwiftUI
#if os(iOS)
import UIKit
#endif
// MARK: - Supporting Types
@@ -227,6 +230,18 @@ struct ContentView: View {
Button("cancel", role: .cancel) {}
}
.alert("Bluetooth Required", isPresented: $viewModel.showBluetoothAlert) {
Button("Settings") {
#if os(iOS)
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
#endif
}
Button("OK", role: .cancel) {}
} message: {
Text(viewModel.bluetoothAlertMessage)
}
.onDisappear {
// Clean up timers
scrollThrottleTimer?.invalidate()