- App.tsx: full navigation (Auth stack + Main tabs with 5 screens) - Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen - HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors - MapScreen: interactive map with layer toggles (6 layers) - MarketplaceScreen: categories (6), products (5), search - ChatScreen: AI chat with quick prompts (4), bot responses - ProfileScreen: user info, stats, menu (9 items), logout - AlertsScreen: alert list with severity, acknowledge - SensorsScreen: sensor list with type filters (6 types), search - ZonesScreen: zone cards with stats - SettingsScreen: language picker (FR/EN/ES/DE), privacy, about - Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n - Hooks: useSensors, useAlerts, useNotifications, useLocation - Components: Card, Button, LoadingSpinner, ErrorBoundary, Header - Services: iotService, notificationService (with axios API client) - Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN) - Theme: colors.ts with full design system (Blue Ocean palette) - Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
126 lines
3.7 KiB
Swift
126 lines
3.7 KiB
Swift
import ExpoModulesCore
|
|
import AVFoundation
|
|
|
|
let cameraKey = "NSCameraUsageDescription"
|
|
let microphoneKey = "NSMicrophoneUsageDescription"
|
|
|
|
protocol BaseCameraRequester {
|
|
var mediaType: AVMediaType { get }
|
|
func permissionWith(status systemStatus: AVAuthorizationStatus) -> [AnyHashable: Any]
|
|
func permissions(for key: String, service: String) -> [AnyHashable: Any]
|
|
func requestAccess(handler: @escaping (Bool) -> Void)
|
|
}
|
|
|
|
extension BaseCameraRequester {
|
|
func permissions(for key: String, service: String) -> [AnyHashable: Any] {
|
|
var systemStatus: AVAuthorizationStatus
|
|
let description = Bundle.main.infoDictionary?[key] as? String
|
|
|
|
if let description {
|
|
systemStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
|
|
} else {
|
|
EXFatal(EXErrorWithMessage("""
|
|
This app is missing \(key),
|
|
so \(service) services will fail. Add this entry to your bundle's Info.plist.
|
|
"""))
|
|
systemStatus = .denied
|
|
}
|
|
|
|
return permissionWith(status: systemStatus)
|
|
}
|
|
|
|
func permissionWith(status systemStatus: AVAuthorizationStatus) -> [AnyHashable: Any] {
|
|
var status: EXPermissionStatus
|
|
|
|
switch systemStatus {
|
|
case .authorized:
|
|
status = EXPermissionStatusGranted
|
|
case .denied, .restricted:
|
|
status = EXPermissionStatusDenied
|
|
case .notDetermined:
|
|
fallthrough
|
|
@unknown default:
|
|
status = EXPermissionStatusUndetermined
|
|
}
|
|
|
|
return [
|
|
"status": status.rawValue
|
|
]
|
|
}
|
|
|
|
func requestAccess(handler: @escaping (Bool) -> Void) {
|
|
AVCaptureDevice.requestAccess(for: mediaType, completionHandler: handler)
|
|
}
|
|
}
|
|
|
|
class CameraOnlyPermissionRequester: NSObject, EXPermissionsRequester, BaseCameraRequester {
|
|
let mediaType: AVMediaType = .video
|
|
|
|
static func permissionType() -> String {
|
|
"camera"
|
|
}
|
|
|
|
func getPermissions() -> [AnyHashable: Any] {
|
|
return permissions(for: cameraKey, service: "video")
|
|
}
|
|
|
|
func requestPermissions(resolver resolve: @escaping EXPromiseResolveBlock, rejecter reject: EXPromiseRejectBlock) {
|
|
requestAccess { [weak self] _ in
|
|
resolve(self?.getPermissions())
|
|
}
|
|
}
|
|
}
|
|
|
|
class CameraPermissionRequester: NSObject, EXPermissionsRequester, BaseCameraRequester {
|
|
let mediaType: AVMediaType = .video
|
|
|
|
static func permissionType() -> String {
|
|
"camera"
|
|
}
|
|
|
|
func getPermissions() -> [AnyHashable: Any] {
|
|
var systemStatus: AVAuthorizationStatus
|
|
var status: EXPermissionStatus
|
|
|
|
let cameraUsuageDescription = Bundle.main.infoDictionary?[cameraKey] as? String
|
|
let microphoneUsuageDescription = Bundle.main.infoDictionary?[microphoneKey] as? String
|
|
|
|
if let cameraUsuageDescription, let microphoneUsuageDescription {
|
|
systemStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
|
|
} else {
|
|
EXFatal(EXErrorWithMessage("""
|
|
This app is missing either NSCameraUsageDescription or NSMicrophoneUsageDescription,
|
|
so audio/video services will fail. Add one of these entries to
|
|
your bundle's Info.plist
|
|
"""))
|
|
systemStatus = .denied
|
|
}
|
|
|
|
return permissionWith(status: systemStatus)
|
|
}
|
|
|
|
func requestPermissions(resolver resolve: @escaping EXPromiseResolveBlock, rejecter reject: EXPromiseRejectBlock) {
|
|
requestAccess { [weak self] _ in
|
|
resolve(self?.getPermissions())
|
|
}
|
|
}
|
|
}
|
|
|
|
class CameraMicrophonePermissionRequester: NSObject, EXPermissionsRequester, BaseCameraRequester {
|
|
let mediaType: AVMediaType = .audio
|
|
|
|
static func permissionType() -> String {
|
|
"microphone"
|
|
}
|
|
|
|
func getPermissions() -> [AnyHashable: Any] {
|
|
return permissions(for: microphoneKey, service: "audio")
|
|
}
|
|
|
|
func requestPermissions(resolver resolve: @escaping EXPromiseResolveBlock, rejecter reject: EXPromiseRejectBlock) {
|
|
requestAccess { [weak self] _ in
|
|
resolve(self?.getPermissions())
|
|
}
|
|
}
|
|
}
|