Apache 2.0 license requires attribution for MijickCamera.
Added:
- 'About' section in Settings with 'Open Source Licenses' link
- LicensesView showing all third-party libraries:
- MijickCamera (Apache 2.0)
- RevenueCat (MIT)
- Links to GitHub repositories
- License type badges
This fulfills the attribution requirement for Apache 2.0 licensed
libraries used in the app.
Major refactor: Replace custom camera implementation with MijickCamera
Added:
- MijickCamera Swift package (v3.0.3)
- MijickTimer dependency (required by MijickCamera)
Changed:
- ContentView now uses MCamera() from MijickCamera
- Ring light wraps around MijickCamera view with padding
- MijickCamera handles all camera logic:
- Permissions
- Capture (photo/video)
- Camera switching
- Orientation/rotation
- Zoom/focus gestures
- Flash
Removed:
- CameraViewModel.swift (replaced by MijickCamera)
- CameraPreview.swift (replaced by MijickCamera)
Kept:
- Ring light background (settings.lightColor)
- Ring size control (settings.ringSize)
- Grid overlay
- Post-capture preview workflow
- Settings view
- Premium features and paywall
- iCloud sync
Benefits:
- Less code to maintain
- Battle-tested camera implementation
- Better rotation handling built-in
- More camera features available (filters, exposure, etc.)
New feature: Rotate preview independent of device orientation
- Rotate button in top control bar (next to Center Stage)
- Cycles through: 0° → 90° → 180° → 270° → 0°
- Yellow highlight when rotation is active
- Icon changes to show current rotation state
Ring light integration:
- Preview background color now matches ring light color
- Letterbox areas (from aspect ratio differences) show ring light
- Placeholder during permission request also uses ring light color
Visual feedback:
- rotate.right (outline) = no rotation
- rotate.right.fill = 90° right
- arrow.up.arrow.down = 180°
- rotate.left.fill = 270° (90° left)
Full accessibility support with labels and hints.
Issues fixed:
1. On app launch, UIDevice.current.orientation may be .unknown or .faceUp
causing no orientation to be set (defaulting to landscape)
2. When phone is flat, orientation was being skipped
Solution:
- Track lastValidOrientation as fallback
- When device orientation is invalid, get from windowScene.interfaceOrientation
- Use last known good orientation if all else fails
- Default to portrait (90°) if nothing else works
This ensures the camera preview starts in the correct orientation
and stays properly rotated during use.
Changes:
1. Preview layer now uses .resizeAspect instead of .resizeAspectFill
- Shows exactly what will be in the captured photo
- No cropping - what you see is what you get
- Ring light naturally fills any letterbox areas
2. Session preset changed from .high to .photo
- Optimized for photo capture
- Consistent 4:3 aspect ratio
- Better quality for selfies
This ensures the preview accurately represents the final photo,
eliminating surprise cropping in captured images.
The preview layer's connection needs its own rotation update, separate
from the capture output connections.
Changes:
- CameraPreviewUIView now listens to UIDevice.orientationDidChangeNotification
- updatePreviewOrientation() updates the preview layer's connection
- Called on layoutSubviews and orientation changes
- Handles faceUp/faceDown/unknown by keeping current orientation
- Uses modern videoRotationAngle API (iOS 17+)
Visual feedback for non-premium users:
- Premium colors show lock overlay with darkened circle
- Crown icon is hollow (outline) when locked, filled when unlocked
- Text is dimmed for locked colors
- Custom color shows rainbow gradient with lock overlay
Interaction behavior:
- Tapping locked premium color opens paywall
- Tapping locked custom color opens paywall
- Non-premium presets (Pure White, Warm Cream) remain fully accessible
- Premium users see unlocked UI with filled crown icons
This helps users:
1. See what premium features are available
2. Easily distinguish free vs premium colors
3. Test both states by toggling ENABLE_DEBUG_PREMIUM env var
Bug: The ring light background was gated behind premium check:
premiumManager.isPremiumUnlocked ? settings.lightColor : white
This blocked ALL colors (even non-premium ones like Warm Cream)
from displaying unless the user was premium.
Fix: Remove the premium gate from the display - the selected color
always shows. Premium enforcement should happen in Settings when
selecting colors, not in displaying them.
Issue: SwiftUI observation wasn't tracking color changes properly
Fixes:
1. Removed intermediate 'settings' computed property in ContentView
- Was breaking SwiftUI's observation chain
- Now access viewModel.settings directly everywhere
2. Added cached lightColorId for immediate UI response
- Similar pattern to ringSize caching
- Ensures SwiftUI tracks the stored property changes
This ensures the ring light color updates immediately when
selecting a different color preset or custom color in settings.
Fixes:
1. Front flash now properly resets after capture
- restorePreviewAfterFlash() called in photo delegate
- Preview no longer stays white after taking photo
2. Device rotation support
- updateVideoOrientation() updates capture connections
- Uses modern videoRotationAngle API (iOS 17+)
- Listens to UIDevice.orientationDidChangeNotification
3. Center Stage support for supported devices
- Detects Center Stage availability on front camera
- Toggle button in top control bar (person.crop.rectangle icon)
- Yellow highlight when enabled
- Updates availability on camera switch
Fixes:
1. Camera preview rounded corners - clipShape now applied before padding
so the preview itself has rounded corners, not the container
2. Debounced slider saves - ringSize and customColor now use debouncing
- Immediate UI update via cached values
- 300ms debounce before cloud save
- Prevents excessive save operations during slider drag
3. Simplified custom color picker to one-step
- ColorPicker styled as a circle button
- Shows current custom color always (no rainbow)
- Tapping opens iOS native color picker directly
- Color applies immediately on selection
- No Apply/Cancel sheet needed
Features:
- Custom color button in Light Color section (rainbow gradient icon)
- Tapping opens color picker sheet with:
- Live color preview
- Native iOS ColorPicker
- Tips for best ring light colors
- Custom color syncs across devices via iCloud
- Premium-gated with crown icon indicator
Storage:
- CustomColorRGB struct for Codable-compatible color storage
- RGB values stored separately in SyncedSettings
- Color converts to/from UIColor for RGB extraction
UI:
- Rainbow gradient when not selected, solid custom color when selected
- Sheet with Apply/Cancel buttons
- Color preview bar at top of picker
Changes:
1. Camera preview now fills available space (not forced square)
- Maintains proper aspect ratio for captured photos
- Controls overlay on top of preview
2. Ring size now limited based on screen dimensions
- Maximum is 1/4 of smaller screen dimension
- Prevents content from shifting off-screen
3. Removed light intensity slider
- Was causing color changes (opacity approach)
- Ring light now always at full brightness
4. Removed crown icon from main screen
- Pro upgrade moved to Settings > Pro section
- Cleaner camera interface
5. Smaller top icons
- Grid and settings buttons use .body font
- Less visual clutter
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