import SwiftUI import Bedrock // MARK: - Timer Options enum TimerOption: String, CaseIterable, Identifiable { case off = "off" case three = "3" case five = "5" case ten = "10" var id: String { rawValue } var displayName: String { switch self { case .off: return String(localized: "Off") case .three: return String(localized: "3s") case .five: return String(localized: "5s") case .ten: return String(localized: "10s") } } var seconds: Int { switch self { case .off: return 0 case .three: return 3 case .five: return 5 case .ten: return 10 } } } // MARK: - Capture Mode enum CaptureMode: String, CaseIterable, Identifiable { case photo = "photo" case video = "video" case boomerang = "boomerang" var id: String { rawValue } var displayName: String { switch self { case .photo: return String(localized: "Photo") case .video: return String(localized: "Video") case .boomerang: return String(localized: "Boomerang") } } var systemImage: String { switch self { case .photo: return "camera.fill" case .video: return "video.fill" case .boomerang: return "arrow.2.squarepath" } } var isPremium: Bool { switch self { case .photo: return false case .video, .boomerang: return true } } } // MARK: - Settings ViewModel /// Observable settings view model with iCloud sync across all devices. /// Uses Bedrock's CloudSyncManager for automatic synchronization. @MainActor @Observable final class SettingsViewModel: RingLightConfigurable { // MARK: - Ring Size Limits /// Minimum ring border size in points static let minRingSize: CGFloat = 10 /// Maximum ring border size in points static let maxRingSize: CGFloat = 120 /// Default ring border size static let defaultRingSize: CGFloat = 40 // MARK: - Cloud Sync Manager /// Manages iCloud sync for settings across all devices private let cloudSync = CloudSyncManager() // MARK: - Observable Properties (Synced) /// Ring border size in points var ringSize: CGFloat { get { cloudSync.data.ringSize } set { updateSettings { $0.ringSize = newValue } } } /// ID of the selected light color preset var lightColorId: String { get { cloudSync.data.lightColorId } set { updateSettings { $0.lightColorId = newValue } } } /// Ring light intensity/opacity (0.5 to 1.0) var lightIntensity: Double { get { cloudSync.data.lightIntensity } set { updateSettings { $0.lightIntensity = newValue } } } /// Whether front flash is enabled (hides preview during capture) var isFrontFlashEnabled: Bool { get { cloudSync.data.isFrontFlashEnabled } set { updateSettings { $0.isFrontFlashEnabled = newValue } } } /// Whether the camera preview is flipped to show a true mirror var isMirrorFlipped: Bool { get { cloudSync.data.isMirrorFlipped } set { updateSettings { $0.isMirrorFlipped = newValue } } } /// Whether skin smoothing filter is enabled var isSkinSmoothingEnabled: Bool { get { cloudSync.data.isSkinSmoothingEnabled } set { updateSettings { $0.isSkinSmoothingEnabled = newValue } } } /// Whether the grid overlay is visible var isGridVisible: Bool { get { cloudSync.data.isGridVisible } set { updateSettings { $0.isGridVisible = newValue } } } /// Current camera zoom factor var currentZoomFactor: Double { get { cloudSync.data.currentZoomFactor } set { updateSettings { $0.currentZoomFactor = newValue } } } // MARK: - Computed Properties /// Convenience property for border width (same as ringSize) var borderWidth: CGFloat { ringSize } var selectedTimer: TimerOption { get { TimerOption(rawValue: cloudSync.data.selectedTimerRaw) ?? .off } set { updateSettings { $0.selectedTimerRaw = newValue.rawValue } } } var selectedCaptureMode: CaptureMode { get { CaptureMode(rawValue: cloudSync.data.selectedCaptureModeRaw) ?? .photo } set { updateSettings { $0.selectedCaptureModeRaw = newValue.rawValue } } } var selectedLightColor: RingLightColor { get { RingLightColor.fromId(lightColorId) } set { lightColorId = newValue.id } } var lightColor: Color { selectedLightColor.color } // MARK: - Sync Status /// Whether iCloud sync is available var iCloudAvailable: Bool { cloudSync.iCloudAvailable } /// Whether iCloud sync is enabled var iCloudEnabled: Bool { get { cloudSync.iCloudEnabled } set { cloudSync.iCloudEnabled = newValue } } /// Last sync date var lastSyncDate: Date? { cloudSync.lastSyncDate } /// Current sync status message var syncStatus: String { cloudSync.syncStatus } /// Whether initial sync has completed var hasCompletedInitialSync: Bool { cloudSync.hasCompletedInitialSync } // MARK: - Initialization init() { // CloudSyncManager handles syncing automatically } // MARK: - Private Methods /// Updates settings and saves to cloud private func updateSettings(_ transform: (inout SyncedSettings) -> Void) { cloudSync.update { settings in transform(&settings) settings.modificationCount += 1 } } // MARK: - Sync Actions /// Forces a sync with iCloud func forceSync() { cloudSync.sync() } /// Resets all settings to defaults func resetToDefaults() { cloudSync.reset() } // MARK: - Validation var isValidConfiguration: Bool { ringSize >= Self.minRingSize && lightIntensity >= 0.5 } } // MARK: - CaptureControlling Conformance extension SettingsViewModel: CaptureControlling { func startCountdown() async { // Countdown handled by CameraViewModel } func performCapture() { // Capture handled by CameraViewModel } func performFlashBurst() async { // Flash handled by CameraViewModel } }