Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions SnapSafe/Screens/AppNavigation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ enum AppDestination: Hashable {
case pinSetup
case pinVerification
case camera
case photoDetail(allPhotos: [PhotoDef], initialIndex: Int)
case photoInfo(PhotoDef)
case photoObfuscation(PhotoDef)
case poisonPillSetupWizard
}
Expand Down Expand Up @@ -85,6 +87,8 @@ extension AppDestination: Identifiable {
case .pinSetup: return "pinSetup"
case .pinVerification: return "pinVerification"
case .camera: return "camera"
case .photoDetail(_, let initialIndex): return "photoDetail_\(initialIndex)"
case .photoInfo(let photoDef): return "photoInfo_\(photoDef.photoName)"
case .photoObfuscation(let photoDef): return "photoObfuscation_\(photoDef.photoName)"
case .poisonPillSetupWizard: return "poisonPillSetupWizard"
}
Expand Down
4 changes: 2 additions & 2 deletions SnapSafe/Screens/Camera/CameraContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ struct CameraContainerView: View {

HStack {
Button(action: {
nav.presentFullScreenCover(.gallery)
nav.navigate(to:.gallery)
}) {
ZStack {
Image(systemName: "photo.on.rectangle")
Expand Down Expand Up @@ -176,7 +176,7 @@ struct CameraContainerView: View {

Spacer()
Button(action: {
nav.presentSheet(.settings)
nav.navigate(to:.settings)
}) {
Image(systemName: "gear")
.font(.system(size: 24))
Expand Down
21 changes: 21 additions & 0 deletions SnapSafe/Screens/Camera/CameraViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ class CameraViewModel: NSObject, ObservableObject {
name: UIApplication.willEnterForegroundNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleAppWillResignActive),
name: UIApplication.willResignActiveNotification,
object: nil
)
}

deinit {
Expand All @@ -132,6 +138,11 @@ class CameraViewModel: NSObject, ObservableObject {
resetZoomLevel()
}

@objc private func handleAppWillResignActive() {
Logger.camera.info("App will resign active, stopping camera")
stopCameraSession()
}

func restartCameraSessionIfNeeded() {
let session = self.session
if !session.isRunning {
Expand All @@ -141,6 +152,16 @@ class CameraViewModel: NSObject, ObservableObject {
}
}
}

func stopCameraSession() {
let session = self.session
if session.isRunning {
Logger.camera.info("Stopping camera session")
DispatchQueue.global(qos: .userInitiated).async {
session.stopRunning()
}
}
}


func checkAndSetupCamera() async {
Expand Down
28 changes: 25 additions & 3 deletions SnapSafe/Screens/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ struct ContentView: View {
.navigationBarHidden(true)
.navigationDestination(for: AppDestination.self) { destination in
navigationDestinationView(for: destination)
.navigationBarHidden(destination != .gallery)
.navigationBarHidden(shouldHideNavigationBar(for: destination))
}
}
.sheet(item: $nav.presentedSheet) { destination in
navigationDestinationView(for: destination)
.securityManaged()
}
.fullScreenCover(item: $nav.presentedFullScreenCover) { destination in
navigationDestinationView(for: destination)
.securityManaged()
}
.securityManaged()
.onAppear {
Expand Down Expand Up @@ -78,23 +80,43 @@ struct ContentView: View {
}
}

// MARK: - Navigation Helper Methods

private func shouldHideNavigationBar(for destination: AppDestination) -> Bool {
switch destination {
case .gallery, .photoObfuscation, .settings:
return false
default:
return true
}
}

// MARK: - Navigation Destination Views

@ViewBuilder
private func navigationDestinationView(for destination: AppDestination) -> some View {
switch destination {
case .settings:
SettingsView()
case .gallery:
SecureGalleryView(onDismiss: {
nav.dismissFullScreenCover()
nav.navigateBack()
})
case .pinSetup:
PINSetupIntroView()
case .pinVerification:
PINVerificationView()
case .camera:
CameraContainerView()
case .photoDetail(let allPhotos, let initialIndex):
EnhancedPhotoDetailView(
allPhotos: allPhotos,
initialIndex: initialIndex,
onDelete: nil,
onDismiss: nil
)
case .photoInfo(let photoDef):
ImageInfoView(photoDef: photoDef)
case .photoObfuscation(let photoDef):
PhotoObfuscationView(photoDef: photoDef, navigator: nav)
case .poisonPillSetupWizard:
Expand Down
55 changes: 20 additions & 35 deletions SnapSafe/Screens/Gallery/SecureGalleryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct SecureGalleryView: View {
@AppStorage("showFaceDetection") private var showFaceDetection = true // Using AppStorage to share with Settings
@StateObject private var viewModel: SecureGalleryViewModel
@Environment(\.dismiss) private var dismiss

@EnvironmentObject private var nav: AppNavigationState

// Callback for dismissing the gallery
let onDismiss: (() -> Void)?

Expand All @@ -47,8 +48,7 @@ struct SecureGalleryView: View {


var body: some View {
NavigationStack {
ZStack {
ZStack {
Group {
if viewModel.photos.isEmpty {
EmptyGalleryView(onDismiss: {
Expand Down Expand Up @@ -83,21 +83,22 @@ struct SecureGalleryView: View {
.navigationTitle(viewModel.navigationTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
// Back button in the leading position
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
if viewModel.isSelectingDecoys {
// Back button in the leading position (only for decoy selection mode)
if viewModel.isSelectingDecoys {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
viewModel.exitDecoyMode()
}
onDismiss?()
dismiss()
}) {
HStack {
Image(systemName: "chevron.left")
onDismiss?()
dismiss()
}) {
HStack {
Image(systemName: "chevron.left")
Text("Back")
}
}
}
}

// Action buttons in the trailing position (simplified for top toolbar)
ToolbarItem(placement: .navigationBarTrailing) {
HStack(spacing: 16) {
Expand Down Expand Up @@ -192,30 +193,15 @@ struct SecureGalleryView: View {
}
.onAppear(perform: viewModel.onAppear)
.onChange(of: viewModel.selectedPhoto) { _, newValue in
viewModel.onSelectedPhotoChange(newValue)
}
.fullScreenCover(item: $viewModel.selectedPhoto) { photoDef in
if let photoDef = newValue {
// Find the index of the selected photo in the photos array
if let initialIndex = viewModel.photos.firstIndex(where: { $0.photoName == photoDef.photoName }) {
EnhancedPhotoDetailView(
allPhotos: viewModel.photos,
initialIndex: initialIndex,
onDelete: { _ in viewModel.onAppear() },
onDismiss: {
viewModel.clearMemoryForAllPhotos()
}
)
} else {
// Fallback if photo not found in array
PhotoDetailView(
photo: photoDef,
onDelete: { _ in viewModel.onAppear() },
onDismiss: {
viewModel.clearMemoryForPhoto(photoDef)
}
)
nav.navigate(to: .photoDetail(allPhotos: viewModel.photos, initialIndex: initialIndex))
}
// Reset selectedPhoto so it can be selected again
viewModel.selectedPhoto = nil
}
}
.alert(
viewModel.deleteAlertTitle,
isPresented: $viewModel.showDeleteConfirmation,
Expand Down Expand Up @@ -256,7 +242,6 @@ struct SecureGalleryView: View {
}
)
}
}

// Photo grid subview
private var photosGridView: some View {
Expand Down
13 changes: 6 additions & 7 deletions SnapSafe/Screens/PhotoDetail/EnhancedPhotoDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,14 @@ struct EnhancedPhotoDetailView: View {
Spacer()
if viewModel.currentIndex < viewModel.photoFiles.count {
PhotoControlsView(
onInfo: { viewModel.showImageInfo = true },
onInfo: {
if let current = viewModel.currentPhotoDef {
nav.presentSheet(.photoInfo(current))
}
},
onObfuscate: {
if let current = viewModel.currentPhotoDef {
nav.presentedFullScreenCover = .photoObfuscation(current)
nav.navigate(to: .photoObfuscation(current))
}
},
onShare: { viewModel.shareCurrentPhoto() },
Expand Down Expand Up @@ -173,10 +177,5 @@ struct EnhancedPhotoDetailView: View {
Text("Are you sure you want to delete this photo? This action cannot be undone.")
}
)
.sheet(isPresented: $viewModel.showImageInfo) {
if let photoDef = viewModel.currentPhotoDef {
ImageInfoView(photoDef: photoDef)
}
}
}
}
68 changes: 33 additions & 35 deletions SnapSafe/Screens/PhotoObfuscation/PhotoObfuscationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,51 @@ struct PhotoObfuscationView: View {
_viewModel = StateObject(wrappedValue:
PhotoObfuscationViewModel(
photoDef: photoDef,
onSave: { _ in navigator.presentedFullScreenCover = nil },
onDismiss: { navigator.presentedFullScreenCover = nil }
onSave: { _ in navigator.navigateBack() },
onDismiss: { navigator.navigateBack() }
)
)
}

private func onDismiss() {
nav.presentedFullScreenCover = nil
nav.navigateBack()
}

var body: some View {
NavigationView {
ZStack {
Color.black
.ignoresSafeArea()

if viewModel.isImageLoading {
ProgressView("Loading image...")
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.foregroundColor(.white)
} else {
imageContent
}
}
.navigationTitle("Photo Obfuscation")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
viewModel.cancel()
onDismiss()
}
ZStack {
Color.black
.ignoresSafeArea()

if viewModel.isImageLoading {
ProgressView("Loading image...")
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.foregroundColor(.white)
} else {
imageContent
}
}
.navigationTitle("Photo Obfuscation")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
viewModel.cancel()
onDismiss()
}

ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
viewModel.saveChanges()
onDismiss()
}
.foregroundColor(.blue)
.fontWeight(.semibold)
.foregroundColor(.white)
}

ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
viewModel.saveChanges()
onDismiss()
}
.foregroundColor(.blue)
.fontWeight(.semibold)
}
.toolbarBackground(Color.black, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
}
.toolbarBackground(Color.black, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.alert("Obscure Faces", isPresented: $viewModel.showObscureConfirmation) {
Button("Cancel", role: .cancel) { }
Button(viewModel.maskActionTitle) {
Expand Down
4 changes: 1 addition & 3 deletions SnapSafe/Screens/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ struct SettingsView: View {
@EnvironmentObject private var nav: AppNavigationState

var body: some View {
NavigationView {
List {
List {

// ABOUT SECTION
Section {
Expand Down Expand Up @@ -209,6 +208,5 @@ struct SettingsView: View {
})
}
}
}
}
}
2 changes: 1 addition & 1 deletion fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ platform :ios do
upload_to_app_store(
skip_screenshots: true,
skip_metadata: true,
submit_for_review: true,
submit_for_review: false,
force: false,
precheck_include_in_app_purchases: false
)
Expand Down
Loading