diff --git a/ios/Ascently.xcodeproj/project.pbxproj b/ios/Ascently.xcodeproj/project.pbxproj index 4212897..bc2a2ca 100644 --- a/ios/Ascently.xcodeproj/project.pbxproj +++ b/ios/Ascently.xcodeproj/project.pbxproj @@ -466,7 +466,7 @@ CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = 4BC9Y2LL4B; DRIVERKIT_DEPLOYMENT_TARGET = 24.6; ENABLE_PREVIEWS = YES; @@ -491,7 +491,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 2.7.2; + MARKETING_VERSION = 2.7.3; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -518,7 +518,7 @@ CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = 4BC9Y2LL4B; DRIVERKIT_DEPLOYMENT_TARGET = 24.6; ENABLE_PREVIEWS = YES; @@ -543,7 +543,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 2.7.2; + MARKETING_VERSION = 2.7.3; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -610,7 +610,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -622,7 +622,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.7.2; + MARKETING_VERSION = 2.7.3; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -641,7 +641,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 49; + CURRENT_PROJECT_VERSION = 50; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -653,7 +653,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.7.2; + MARKETING_VERSION = 2.7.3; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate index 0517efe..710b252 100644 Binary files a/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/Ascently/Views/AddEdit/AddEditProblemView.swift b/ios/Ascently/Views/AddEdit/AddEditProblemView.swift index b71da70..8ce67d7 100644 --- a/ios/Ascently/Views/AddEdit/AddEditProblemView.swift +++ b/ios/Ascently/Views/AddEdit/AddEditProblemView.swift @@ -244,7 +244,7 @@ struct AddEditProblemView: View { let tempImagePaths = imagePaths.filter { !$0.isEmpty && !imagePaths.contains($0) } for imagePath in tempImagePaths { - ImageManager.shared.deleteImage(atPath: imagePath) + _ = ImageManager.shared.deleteImage(atPath: imagePath) } let newImagePaths = imagePaths.filter { !$0.isEmpty } diff --git a/ios/Ascently/Views/SettingsView.swift b/ios/Ascently/Views/SettingsView.swift index 8ff94f2..3220ac1 100644 --- a/ios/Ascently/Views/SettingsView.swift +++ b/ios/Ascently/Views/SettingsView.swift @@ -682,8 +682,8 @@ struct ExportDataView: View { private func cleanupTempFile() { if let fileURL = tempFileURL { - let logTag = Self.logTag // Capture before entering async closure - // Clean up after a delay to ensure sharing is complete + let logTag = Self.logTag + DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { try? FileManager.default.removeItem(at: fileURL) AppLogger.debug( @@ -752,80 +752,84 @@ struct SyncSection: View { } .foregroundColor(.primary) - if syncService.isConfigured { - - // Sync Now - only show if connected + // Sync Now - show with proper opacity when not available + Button(action: { if syncService.isConnected { - Button(action: { - performSync() - }) { - HStack { - if syncService.isSyncing { - ProgressView() - .scaleEffect(0.8) - Text("Syncing...") - .foregroundColor(.secondary) - } else { - Image(systemName: "arrow.triangle.2.circlepath") - .foregroundColor(.green) - Text("Sync Now") - Spacer() - if let lastSync = syncService.lastSyncTime { - Text( - RelativeDateTimeFormatter().localizedString( - for: lastSync, relativeTo: Date()) - ) - .font(.caption) - .foregroundColor(.secondary) - } - } - } - } - .disabled(syncService.isSyncing) - .foregroundColor(.primary) + performSync() } - - // Auto-sync configuration - always visible for testing + }) { HStack { - VStack(alignment: .leading) { - Text("Auto-sync") - Text("Sync automatically on app launch and data changes") + if syncService.isSyncing { + ProgressView() + .scaleEffect(0.8) + Text("Syncing...") + .foregroundColor(.secondary) + } else { + Image(systemName: "arrow.triangle.2.circlepath") + .foregroundColor(syncService.isConnected ? .green : .secondary) + Text("Sync Now") + .foregroundColor(syncService.isConnected ? .primary : .secondary) + Spacer() + if let lastSync = syncService.lastSyncTime { + Text( + RelativeDateTimeFormatter().localizedString( + for: lastSync, relativeTo: Date()) + ) .font(.caption) .foregroundColor(.secondary) - } - Spacer() - Toggle( - "", - isOn: Binding( - get: { syncService.isAutoSyncEnabled }, - set: { syncService.isAutoSyncEnabled = $0 } - ) - ) - .disabled(!syncService.isConnected) - } - .foregroundColor(.primary) - - // Disconnect option - only show if connected - if syncService.isConnected { - Button(action: { - showingDisconnectAlert = true - }) { - HStack { - Image(systemName: "power") - .foregroundColor(.orange) - Text("Disconnect") - Spacer() } } - .foregroundColor(.primary) } + } + .disabled(!syncService.isConnected || syncService.isSyncing) + .opacity(syncService.isConfigured ? 1.0 : 0.6) - if let error = syncService.syncError { + // Auto-sync configuration + HStack { + VStack(alignment: .leading) { + Text("Auto-sync") + Text("Sync automatically on app launch and data changes") + .font(.caption) + .foregroundColor(.secondary) + } + Spacer() + Toggle( + "", + isOn: Binding( + get: { syncService.isAutoSyncEnabled }, + set: { syncService.isAutoSyncEnabled = $0 } + ) + ) + .disabled(!syncService.isConnected) + } + .opacity(syncService.isConfigured ? 1.0 : 0.6) + + // Disconnect option + Button(action: { + if syncService.isConnected { + showingDisconnectAlert = true + } + }) { + HStack { + Image(systemName: "power") + .foregroundColor(syncService.isConnected ? .orange : .secondary) + Text("Disconnect") + .foregroundColor(syncService.isConnected ? .primary : .secondary) + Spacer() + } + } + .disabled(!syncService.isConnected) + .opacity(syncService.isConfigured ? 1.0 : 0.6) + + // Error message + if let error = syncService.syncError { + HStack { Text(error) .font(.caption) .foregroundColor(.red) - .padding(.leading, 24) + Spacer() } + .padding(.leading, 24) } } .alert("Disconnect from Server", isPresented: $showingDisconnectAlert) { @@ -1215,34 +1219,32 @@ struct HealthKitSection: View { .foregroundColor(.secondary) } } else { - Toggle( - isOn: Binding( - get: { healthKitService.isEnabled }, - set: { newValue in - if newValue && !healthKitService.isAuthorized { - isRequestingAuthorization = true - Task { - do { - try await healthKitService.requestAuthorization() - await MainActor.run { - healthKitService.setEnabled(true) - isRequestingAuthorization = false - } - } catch { - await MainActor.run { - showingAuthorizationError = true - isRequestingAuthorization = false - } + Toggle(isOn: Binding( + get: { healthKitService.isEnabled }, + set: { newValue in + if newValue && !healthKitService.isAuthorized { + isRequestingAuthorization = true + Task { + do { + try await healthKitService.requestAuthorization() + await MainActor.run { + healthKitService.setEnabled(true) + isRequestingAuthorization = false + } + } catch { + await MainActor.run { + showingAuthorizationError = true + isRequestingAuthorization = false } } - } else if newValue { - healthKitService.setEnabled(true) - } else { - healthKitService.setEnabled(false) } + } else if newValue { + healthKitService.setEnabled(true) + } else { + healthKitService.setEnabled(false) } - ) - ) { + } + )) { HStack { Image(systemName: "heart.fill") .foregroundColor(.red) @@ -1250,14 +1252,15 @@ struct HealthKitSection: View { } } .disabled(isRequestingAuthorization) - + if healthKitService.isEnabled { - VStack(alignment: .leading, spacing: 4) { - Text( - "Climbing sessions will be recorded as workouts in Apple Health" - ) - .font(.caption) - .foregroundColor(.secondary) + HStack { + VStack(alignment: .leading, spacing: 4) { + Text("Climbing sessions will be recorded as workouts in Apple Health") + .font(.caption) + .foregroundColor(.secondary) + } + Spacer() } } }