SelfieRingLight/SelfieRingLight/Shared/Storage/SyncedSettings.swift
Matt Bruce ef15a8c21a Add post-capture workflow with preview, edit, and share
Features:
- Full-screen PostCapturePreviewView after photo/video capture
- Auto-save to Photo Library (on by default, configurable in Settings)
- Toast notification when saved
- Retake button to discard and return to camera
- Share button with native iOS Share Sheet
- Edit mode with smoothing and glow intensity sliders
- Premium tools teaser in edit view
- Video/boomerang auto-playback with loop support

Settings:
- Added 'Auto-Save' toggle in Capture section
- Syncs across devices via iCloud

Architecture:
- CapturedMedia enum for photo/video/boomerang types
- ShareSheet UIViewControllerRepresentable wrapper
- Toast system in CameraViewModel
2026-01-02 13:05:27 -06:00

140 lines
4.4 KiB
Swift

import Foundation
import SwiftUI
import Bedrock
// MARK: - Synced Settings Data
/// Settings data structure that syncs across all devices via iCloud.
/// Conforms to `PersistableData` for use with Bedrock's `CloudSyncManager`.
struct SyncedSettings: PersistableData, Sendable {
// MARK: - PersistableData Requirements
static var dataIdentifier: String { "selfieRingLightSettings" }
static var empty: SyncedSettings {
SyncedSettings()
}
/// Sync priority based on modification count - higher means more changes made.
/// This ensures the most actively used device's settings win in conflicts.
var syncPriority: Int {
modificationCount
}
var lastModified: Date = .now
// MARK: - Settings Properties
/// How many times settings have been modified (for sync priority)
var modificationCount: Int = 0
/// Ring border size in points (stored as Double for Codable compatibility)
var ringSizeValue: Double = 40
/// ID of the selected light color preset
var lightColorId: String = "pureWhite"
/// Ring light intensity/opacity (0.5 to 1.0)
var lightIntensity: Double = 1.0
/// Whether front flash is enabled (hides preview during capture)
var isFrontFlashEnabled: Bool = true
/// Whether the camera preview is flipped to show a true mirror
var isMirrorFlipped: Bool = false
/// Whether skin smoothing filter is enabled
var isSkinSmoothingEnabled: Bool = true
/// Selected self-timer option raw value
var selectedTimerRaw: String = "off"
/// Whether the grid overlay is visible
var isGridVisible: Bool = false
/// Current camera zoom factor
var currentZoomFactor: Double = 1.0
/// Selected capture mode raw value
var selectedCaptureModeRaw: String = "photo"
/// Whether captures are auto-saved to Photo Library
var isAutoSaveEnabled: Bool = true
// MARK: - Computed Properties
/// Ring size as CGFloat (convenience accessor)
var ringSize: CGFloat {
get { CGFloat(ringSizeValue) }
set { ringSizeValue = Double(newValue) }
}
// MARK: - Initialization
init() {}
init(
ringSize: CGFloat,
lightColorId: String,
lightIntensity: Double,
isFrontFlashEnabled: Bool,
isMirrorFlipped: Bool,
isSkinSmoothingEnabled: Bool,
selectedTimerRaw: String,
isGridVisible: Bool,
currentZoomFactor: Double,
selectedCaptureModeRaw: String,
modificationCount: Int = 0
) {
self.ringSizeValue = Double(ringSize)
self.lightColorId = lightColorId
self.lightIntensity = lightIntensity
self.isFrontFlashEnabled = isFrontFlashEnabled
self.isMirrorFlipped = isMirrorFlipped
self.isSkinSmoothingEnabled = isSkinSmoothingEnabled
self.selectedTimerRaw = selectedTimerRaw
self.isGridVisible = isGridVisible
self.currentZoomFactor = currentZoomFactor
self.selectedCaptureModeRaw = selectedCaptureModeRaw
self.modificationCount = modificationCount
self.lastModified = .now
}
// MARK: - Codable
enum CodingKeys: String, CodingKey {
case modificationCount
case lastModified
case ringSizeValue
case lightColorId
case lightIntensity
case isFrontFlashEnabled
case isMirrorFlipped
case isSkinSmoothingEnabled
case selectedTimerRaw
case isGridVisible
case currentZoomFactor
case selectedCaptureModeRaw
case isAutoSaveEnabled
}
}
// MARK: - Equatable
extension SyncedSettings: Equatable {
static func == (lhs: SyncedSettings, rhs: SyncedSettings) -> Bool {
lhs.ringSizeValue == rhs.ringSizeValue &&
lhs.lightColorId == rhs.lightColorId &&
lhs.lightIntensity == rhs.lightIntensity &&
lhs.isFrontFlashEnabled == rhs.isFrontFlashEnabled &&
lhs.isMirrorFlipped == rhs.isMirrorFlipped &&
lhs.isSkinSmoothingEnabled == rhs.isSkinSmoothingEnabled &&
lhs.selectedTimerRaw == rhs.selectedTimerRaw &&
lhs.isGridVisible == rhs.isGridVisible &&
lhs.currentZoomFactor == rhs.currentZoomFactor &&
lhs.selectedCaptureModeRaw == rhs.selectedCaptureModeRaw &&
lhs.isAutoSaveEnabled == rhs.isAutoSaveEnabled
}
}