Files
Eric FELIXINE e30ae8ed09 feat(smart-app): implement complete mobile app MVP
- 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
2026-06-01 18:00:35 -04:00

229 lines
4.9 KiB
Swift

// Copyright 2022-present 650 Industries. All rights reserved.
import ExpoModulesCore
import MobileCoreServices
import PhotosUI
internal let DEFAULT_QUALITY = 0.2
internal let MAXIMUM_QUALITY = 1.0
internal let UNLIMITED_SELECTION = 0
internal let SINGLE_SELECTION = 1
internal struct ImagePickerOptions: Record {
@Field
var allowsEditing: Bool = false
@Field
var aspect: [Double]
@Field
var quality: Double?
@Field
var mediaTypes: MediaType = .images
@Field
var exif: Bool
@Field
var base64: Bool = false
@Field
var videoExportPreset: VideoExportPreset = .passthrough
@Field
var videoQuality: VideoQuality = .typeHigh
@Field
var videoMaxDuration: Double = 0
@Field
var presentationStyle: PresentationStyle = .automatic
@Field
var preferredAssetRepresentationMode: PreferredAssetRepresentationMode = .automatic
@Field
var cameraType: CameraType = .back
@Field
var allowsMultipleSelection: Bool = false
@Field
var selectionLimit: Int = UNLIMITED_SELECTION
@Field
var orderedSelection: Bool = false
}
internal enum PresentationStyle: String, EnumArgument {
case fullScreen
case pageSheet
case formSheet
case currentContext
case overFullScreen
case overCurrentContext
case popover
case none
case automatic
func toPresentationStyle() -> UIModalPresentationStyle {
switch self {
case .fullScreen:
return .fullScreen
case .pageSheet:
return .pageSheet
case .formSheet:
return .formSheet
case .currentContext:
return .currentContext
case .overFullScreen:
return .overFullScreen
case .overCurrentContext:
return .overCurrentContext
case .popover:
return .popover
case .none:
return .none
case .automatic:
if #available(iOS 13.0, *) {
return .automatic
}
// default prior iOS 13
return .fullScreen
}
}
}
internal enum PreferredAssetRepresentationMode: String, EnumArgument {
case automatic
case compatible
case current
@available(iOS 14.0, *)
func toAssetRepresentationMode() -> PHPickerConfiguration.AssetRepresentationMode {
switch self {
case .automatic:
return .automatic
case .compatible:
return .compatible
case .current:
return .current
}
}
}
internal enum VideoQuality: Int, EnumArgument {
case typeHigh = 0
case typeMedium = 1
case typeLow = 2
case type640x480 = 3
case typeIFrame1280x720 = 4
case typeIFrame960x540 = 5
func toQualityType() -> UIImagePickerController.QualityType {
switch self {
case .typeHigh:
return .typeHigh
case .typeMedium:
return .typeMedium
case .typeLow:
return .typeLow
case .type640x480:
return .type640x480
case .typeIFrame1280x720:
return .typeIFrame1280x720
case .typeIFrame960x540:
return .typeIFrame960x540
}
}
}
internal enum MediaType: String, EnumArgument {
case all = "All"
case videos = "Videos"
case images = "Images"
func toArray() -> [String] {
switch self {
case .images:
return [kUTTypeImage as String]
case .videos:
return [kUTTypeMovie as String]
case .all:
return [kUTTypeImage as String, kUTTypeMovie as String]
}
}
func requiresMicrophonePermission() -> Bool {
switch self {
case .images:
return false
case .videos:
return true
case .all:
return true
}
}
@available(iOS 14, *)
func toPickerFilter() -> PHPickerFilter {
// TODO: (barthap) Maybe add support for live photos
switch self {
case .images:
return .images
case .videos:
return .videos
case .all:
return .any(of: [.images, .videos])
}
}
}
internal enum VideoExportPreset: Int, EnumArgument {
case passthrough = 0
case lowQuality = 1
case mediumQuality = 2
case highestQuality = 3
case h264_640x480 = 4
case h264_960x540 = 5
case h264_1280x720 = 6
case h264_1920x1080 = 7
case h264_3840x2160 = 8
case hevc_1920x1080 = 9
case hevc_3840_2160 = 10
func toAVAssetExportPreset() -> String {
switch self {
case .passthrough:
return AVAssetExportPresetPassthrough
case .lowQuality:
return AVAssetExportPresetLowQuality
case .mediumQuality:
return AVAssetExportPresetMediumQuality
case .highestQuality:
return AVAssetExportPresetHighestQuality
case .h264_640x480:
return AVAssetExportPreset640x480
case .h264_960x540:
return AVAssetExportPreset960x540
case .h264_1280x720:
return AVAssetExportPreset1280x720
case .h264_1920x1080:
return AVAssetExportPreset1920x1080
case .h264_3840x2160:
return AVAssetExportPreset3840x2160
case .hevc_1920x1080:
return AVAssetExportPresetHEVC1920x1080
case .hevc_3840_2160:
return AVAssetExportPresetHEVC3840x2160
}
}
}
internal enum CameraType: String, EnumArgument {
case back
case front
}