diff --git a/PortfolioJournal.xcodeproj/project.pbxproj b/PortfolioJournal.xcodeproj/project.pbxproj index f2e1ad3..4a9e54b 100644 --- a/PortfolioJournal.xcodeproj/project.pbxproj +++ b/PortfolioJournal.xcodeproj/project.pbxproj @@ -768,6 +768,7 @@ STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; @@ -809,6 +810,7 @@ STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; diff --git a/PortfolioJournal/Models/CoreDataStack.swift b/PortfolioJournal/Models/CoreDataStack.swift index a8f3bc3..890fe91 100644 --- a/PortfolioJournal/Models/CoreDataStack.swift +++ b/PortfolioJournal/Models/CoreDataStack.swift @@ -820,7 +820,7 @@ class CoreDataStack: ObservableObject { guard Self.cloudKitEnabled else { completion(0); return } let entities = ["Account", "Category", "InvestmentSource", "Snapshot", "Goal"] let context = newBackgroundContext() - context.perform { [weak self] in + context.perform { var totalTouched = 0 for entityName in entities { let request = NSFetchRequest(entityName: entityName) diff --git a/PortfolioJournal/Services/AppIntentsSupport.swift b/PortfolioJournal/Services/AppIntentsSupport.swift index 0851c14..c9ad920 100644 --- a/PortfolioJournal/Services/AppIntentsSupport.swift +++ b/PortfolioJournal/Services/AppIntentsSupport.swift @@ -60,14 +60,14 @@ struct SourceEntity: AppEntity { struct SourceEntityQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [SourceEntity] { - SharedQuickUpdateStore.readMirror() + await MainActor.run { SharedQuickUpdateStore.readMirror() } .filter { identifiers.contains($0.id) } .map(SourceEntity.init) } func suggestedEntities() async throws -> [SourceEntity] { // Pending-first, mirroring the Share Extension's ordering. - SharedQuickUpdateStore.readMirror() + await MainActor.run { SharedQuickUpdateStore.readMirror() } .sorted { a, b in if a.updatedThisMonth != b.updatedThisMonth { return !a.updatedThisMonth } return a.name.localizedCaseInsensitiveCompare(b.name) == .orderedAscending diff --git a/PortfolioJournal/Services/ChartShareService.swift b/PortfolioJournal/Services/ChartShareService.swift index 6193c62..c8e49d1 100644 --- a/PortfolioJournal/Services/ChartShareService.swift +++ b/PortfolioJournal/Services/ChartShareService.swift @@ -124,7 +124,7 @@ struct ChartShareCardView: View { .font(.headline) Text("\(title) · \(Self.dateLabel)") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() } @@ -138,15 +138,15 @@ struct ChartShareCardView: View { VStack(alignment: .leading, spacing: 3) { Text(stats[i].label) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(stats[i].value) .font(.subheadline.weight(.semibold)) - .foregroundColor(stats[i].color) + .foregroundStyle(stats[i].color) } .padding(.horizontal, 12) .padding(.vertical, 8) .background(Color(.systemGray6)) - .cornerRadius(8) + .clipShape(RoundedRectangle(cornerRadius: 8)) } Spacer() } @@ -161,7 +161,7 @@ struct ChartShareCardView: View { .font(.footnote.weight(.medium)) Text(verbatim: "portfoliojournal.app") .font(.footnote) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } Spacer() if let qrCodeImage { @@ -173,7 +173,7 @@ struct ChartShareCardView: View { .frame(width: 64, height: 64) Text(String(localized: "chart_share_scan")) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } diff --git a/PortfolioJournal/Services/ReviewPromptService.swift b/PortfolioJournal/Services/ReviewPromptService.swift index 242de62..a851e44 100644 --- a/PortfolioJournal/Services/ReviewPromptService.swift +++ b/PortfolioJournal/Services/ReviewPromptService.swift @@ -15,18 +15,18 @@ final class ReviewPromptService { private let minDaysBetweenPrompts = 30 private let userDefaults: UserDefaults private let dateProvider: () -> Date - private let reviewRequestHandler: () -> Void + private let reviewRequestHandler: @MainActor () -> Void private init() { userDefaults = .standard dateProvider = Date.init - reviewRequestHandler = ReviewPromptService.defaultReviewRequestHandler + reviewRequestHandler = { ReviewPromptService.defaultReviewRequestHandler() } } init( userDefaults: UserDefaults, dateProvider: @escaping () -> Date, - reviewRequestHandler: @escaping () -> Void + reviewRequestHandler: @escaping @MainActor () -> Void ) { self.userDefaults = userDefaults self.dateProvider = dateProvider @@ -93,6 +93,7 @@ final class ReviewPromptService { userDefaults.set(0, forKey: checkInCountKey) } + @MainActor private static func defaultReviewRequestHandler() { guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return } if #available(iOS 18.0, *) { diff --git a/PortfolioJournal/Utilities/DashboardLayoutStore.swift b/PortfolioJournal/Utilities/DashboardLayoutStore.swift index 9e4654e..4baaf45 100644 --- a/PortfolioJournal/Utilities/DashboardLayoutStore.swift +++ b/PortfolioJournal/Utilities/DashboardLayoutStore.swift @@ -74,7 +74,7 @@ enum DashboardLayoutStore { var merged: [DashboardSectionConfig] = [] for config in decoded { - if let section = DashboardSection(rawValue: config.id) { + if DashboardSection(rawValue: config.id) != nil { merged.append(config) } else { continue diff --git a/PortfolioJournal/Utilities/Extensions/Color+Extensions.swift b/PortfolioJournal/Utilities/Extensions/Color+Extensions.swift index 94ab1de..0ebb4da 100644 --- a/PortfolioJournal/Utilities/Extensions/Color+Extensions.swift +++ b/PortfolioJournal/Utilities/Extensions/Color+Extensions.swift @@ -167,6 +167,7 @@ extension Color { // MARK: - Gradient Extensions +@MainActor extension LinearGradient { static let appPrimaryGradient = LinearGradient( colors: [Color.appPrimary, Color.appPrimary.lighter()], diff --git a/PortfolioJournal/Utilities/MonthlyCheckInStore.swift b/PortfolioJournal/Utilities/MonthlyCheckInStore.swift index 9ca81ef..60ee0c6 100644 --- a/PortfolioJournal/Utilities/MonthlyCheckInStore.swift +++ b/PortfolioJournal/Utilities/MonthlyCheckInStore.swift @@ -66,7 +66,7 @@ enum MonthlyCheckInStore { } static func entry(for date: Date) -> MonthlyCheckInEntry? { - fetchEntry(for: monthKey(for: date)).map(makeCheckInEntry) + fetchEntry(for: monthKey(for: date)).map { makeCheckInEntry($0) } } static func allEntries() -> [(date: Date, entry: MonthlyCheckInEntry)] { diff --git a/PortfolioJournal/Utilities/SnapshotGapDetector.swift b/PortfolioJournal/Utilities/SnapshotGapDetector.swift index 9a2f65a..1a62e02 100644 --- a/PortfolioJournal/Utilities/SnapshotGapDetector.swift +++ b/PortfolioJournal/Utilities/SnapshotGapDetector.swift @@ -31,7 +31,6 @@ enum SnapshotGapDetector { /// Only months strictly between two known snapshots count — a source's /// trailing "not updated in a while" is handled elsewhere (pending updates). static func detectGaps(sources: [InvestmentSource], snapshots: [Snapshot]) -> [Gap] { - let calendar = Calendar.current let snapshotsBySource = Dictionary(grouping: snapshots) { $0.source?.id } var gaps: [Gap] = [] diff --git a/PortfolioJournal/ViewModels/SettingsViewModel.swift b/PortfolioJournal/ViewModels/SettingsViewModel.swift index 96a66c1..a563b7f 100644 --- a/PortfolioJournal/ViewModels/SettingsViewModel.swift +++ b/PortfolioJournal/ViewModels/SettingsViewModel.swift @@ -239,8 +239,8 @@ class SettingsViewModel: ObservableObject { exportStatus = "Export complete" isExporting = false showingExportOptions = false - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in - self?.shareItem = ShareItem(url: tempURL) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self.shareItem = ShareItem(url: tempURL) } FirebaseService.shared.logExportAttempt(format: format.rawValue, success: true) } diff --git a/PortfolioJournal/Views/Accounts/AccountEditorView.swift b/PortfolioJournal/Views/Accounts/AccountEditorView.swift index 27fcc97..8b04238 100644 --- a/PortfolioJournal/Views/Accounts/AccountEditorView.swift +++ b/PortfolioJournal/Views/Accounts/AccountEditorView.swift @@ -21,11 +21,11 @@ struct AccountEditorView: View { if isEditingDefaultAccount { HStack { Text(Account.defaultAccountName) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() Image(systemName: "lock.fill") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } else { TextField("Account name", text: $name) @@ -40,7 +40,7 @@ struct AccountEditorView: View { } footer: { if let errorMessage { Text(errorMessage) - .foregroundColor(.negativeRed) + .foregroundStyle(Color.negativeRed) } else if isEditingDefaultAccount { Text("The Default account name cannot be changed.") } @@ -62,10 +62,10 @@ struct AccountEditorView: View { .navigationTitle(account == nil ? "New Account" : "Edit Account") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Save") { saveAccount() } .disabled(!isValid) } diff --git a/PortfolioJournal/Views/Accounts/AccountsView.swift b/PortfolioJournal/Views/Accounts/AccountsView.swift index e8b0580..4cd1297 100644 --- a/PortfolioJournal/Views/Accounts/AccountsView.swift +++ b/PortfolioJournal/Views/Accounts/AccountsView.swift @@ -1,4 +1,5 @@ import SwiftUI +import CoreData struct AccountsView: View { @EnvironmentObject private var iapService: IAPService @@ -33,17 +34,17 @@ struct AccountsView: View { if account.isDefaultAccount { Image(systemName: "star.fill") .font(.caption2) - .foregroundColor(.appWarning) + .foregroundStyle(Color.appWarning) } } Text(account.currencyCode ?? AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currency) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() if accountStore.selectedAccount?.safeId == account.safeId && !accountStore.showAllAccounts { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } } } @@ -92,7 +93,7 @@ struct AccountsView: View { Text("The Default account cannot be deleted. You must keep at least one account.") } .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button { if accountStore.canAddAccount() { showingAddAccount = true diff --git a/PortfolioJournal/Views/Components/LoadingView.swift b/PortfolioJournal/Views/Components/LoadingView.swift index 54a581a..67edb07 100644 --- a/PortfolioJournal/Views/Components/LoadingView.swift +++ b/PortfolioJournal/Views/Components/LoadingView.swift @@ -10,7 +10,7 @@ struct LoadingView: View { Text(message) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(.systemBackground)) @@ -30,14 +30,14 @@ struct AppLaunchLoadingView: View { .frame(width: 140, height: 140) .padding(16) .background(Color.appPrimary.opacity(0.08)) - .cornerRadius(28) + .clipShape(RoundedRectangle(cornerRadius: 28)) ProgressView() .scaleEffect(1.2) Text(messageKey) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() } @@ -82,27 +82,27 @@ struct SkeletonCardView: View { VStack(alignment: .leading, spacing: 12) { SkeletonView() .frame(width: 100, height: 16) - .cornerRadius(4) + .clipShape(RoundedRectangle(cornerRadius: 4)) SkeletonView() .frame(height: 32) - .cornerRadius(4) + .clipShape(RoundedRectangle(cornerRadius: 4)) HStack { SkeletonView() .frame(width: 80, height: 14) - .cornerRadius(4) + .clipShape(RoundedRectangle(cornerRadius: 4)) Spacer() SkeletonView() .frame(width: 60, height: 14) - .cornerRadius(4) + .clipShape(RoundedRectangle(cornerRadius: 4)) } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } @@ -119,14 +119,14 @@ struct EmptyStateView: View { VStack(spacing: 20) { Image(systemName: icon) .font(.system(size: 60)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(title) .font(.title2.weight(.semibold)) Text(message) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) @@ -134,11 +134,11 @@ struct EmptyStateView: View { Button(action: action) { Text(actionTitle) .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .padding() .frame(maxWidth: 200) .background(Color.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } } @@ -156,14 +156,14 @@ struct ErrorView: View { VStack(spacing: 16) { Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 48)) - .foregroundColor(.appWarning) + .foregroundStyle(Color.appWarning) Text("Something went wrong") .font(.headline) Text(message) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) @@ -174,8 +174,8 @@ struct ErrorView: View { .padding(.horizontal, 20) .padding(.vertical, 10) .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(20) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 20)) } } } @@ -199,7 +199,7 @@ struct SuccessView: View { Image(systemName: "checkmark.circle.fill") .font(.system(size: 60)) - .foregroundColor(.positiveGreen) + .foregroundStyle(Color.positiveGreen) } Text(title) @@ -207,7 +207,7 @@ struct SuccessView: View { Text(message) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) @@ -215,11 +215,11 @@ struct SuccessView: View { Button(action: action) { Text("Continue") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .padding() .frame(maxWidth: 200) .background(Color.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } } @@ -255,14 +255,14 @@ struct ToastView: View { var body: some View { HStack(spacing: 12) { Image(systemName: type.icon) - .foregroundColor(type.color) + .foregroundStyle(type.color) Text(message) .font(.subheadline) } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.1), radius: 10, y: 5) } } diff --git a/PortfolioJournal/Views/Components/ProgressRing.swift b/PortfolioJournal/Views/Components/ProgressRing.swift index cb713ed..fa778b3 100644 --- a/PortfolioJournal/Views/Components/ProgressRing.swift +++ b/PortfolioJournal/Views/Components/ProgressRing.swift @@ -21,7 +21,7 @@ struct ProgressRing: View { .animation(.easeOut(duration: 0.6), value: clamped) Text("\(Int((clamped * 100).rounded()))%") .font(.system(size: size * 0.26, weight: .bold, design: .rounded)) - .foregroundColor(tint) + .foregroundStyle(tint) .minimumScaleFactor(0.7) } .frame(width: size, height: size) diff --git a/PortfolioJournal/Views/Dashboard/CategoryBreakdown.swift b/PortfolioJournal/Views/Dashboard/CategoryBreakdown.swift index 18d914b..b05d2f9 100644 --- a/PortfolioJournal/Views/Dashboard/CategoryBreakdown.swift +++ b/PortfolioJournal/Views/Dashboard/CategoryBreakdown.swift @@ -17,7 +17,7 @@ struct CategoryBreakdownCard: View { } label: { Text("See All") .font(.subheadline) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } } @@ -27,7 +27,7 @@ struct CategoryBreakdownCard: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } } @@ -55,7 +55,7 @@ struct CategoryRowView: View { Image(systemName: category.icon) .font(.system(size: 14)) - .foregroundColor(Color(hex: category.colorHex) ?? .gray) + .foregroundStyle(Color(hex: category.colorHex) ?? .gray) } // Name and percentage @@ -65,12 +65,12 @@ struct CategoryRowView: View { Text(category.formattedPercentage) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) if let target = targetPercentage { Text("Target \(String(format: "%.0f%%", target)) | Drift \(driftText ?? "")") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -84,10 +84,10 @@ struct CategoryRowView: View { HStack(spacing: 4) { Text("CAGR") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(category.metrics.formattedCAGR) .font(.caption.weight(.semibold)) - .foregroundColor(category.metrics.cagr >= 0 ? .positiveGreen : .negativeRed) + .foregroundStyle(category.metrics.cagr >= 0 ? Color.positiveGreen : Color.negativeRed) } } } @@ -122,7 +122,7 @@ struct CategoryProgressBar: View { Text(category.formattedPercentage) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } GeometryReader { geometry in @@ -130,12 +130,12 @@ struct CategoryProgressBar: View { Rectangle() .fill(Color.gray.opacity(0.1)) .frame(height: 6) - .cornerRadius(3) + .clipShape(RoundedRectangle(cornerRadius: 3)) Rectangle() .fill(Color(hex: category.colorHex) ?? .gray) .frame(width: geometry.size.width * progress, height: 6) - .cornerRadius(3) + .clipShape(RoundedRectangle(cornerRadius: 3)) } } .frame(height: 6) @@ -166,7 +166,7 @@ struct SimpleCategoryList: View { Text("(\(category.formattedPercentage))") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } diff --git a/PortfolioJournal/Views/Dashboard/DataGapsView.swift b/PortfolioJournal/Views/Dashboard/DataGapsView.swift index 630bcfb..9fcc30e 100644 --- a/PortfolioJournal/Views/Dashboard/DataGapsView.swift +++ b/PortfolioJournal/Views/Dashboard/DataGapsView.swift @@ -13,27 +13,27 @@ struct DataGapsBanner: View { HStack(spacing: 12) { Image(systemName: "exclamationmark.triangle.fill") .font(.title3) - .foregroundColor(.orange) + .foregroundStyle(.orange) VStack(alignment: .leading, spacing: 2) { Text(String(format: String(localized: "gaps_banner_title"), totalMissingMonths)) .font(.subheadline.weight(.semibold)) - .foregroundColor(.primary) + .foregroundStyle(.primary) .multilineTextAlignment(.leading) Text("Fill in missing data") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Image(systemName: "chevron.right") .font(.caption.weight(.semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding(14) .background(Color(.secondarySystemGroupedBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .overlay( RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius, style: .continuous) .stroke(Color.orange.opacity(0.35), lineWidth: 1) @@ -44,7 +44,7 @@ struct DataGapsBanner: View { Button(action: onDismiss) { Image(systemName: "xmark.circle.fill") .font(.body) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .padding(6) } .buttonStyle(.plain) @@ -89,7 +89,7 @@ struct DataGapsSheet: View { .navigationTitle("Missing Data") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Done") { dismiss() } } } @@ -109,7 +109,7 @@ struct DataGapsSheet: View { Spacer() Text(String(format: String(localized: "gaps_missing_count"), gap.missingMonths)) .font(.caption.weight(.semibold)) - .foregroundColor(.orange) + .foregroundStyle(.orange) .padding(.horizontal, 8) .padding(.vertical, 3) .background(Color.orange.opacity(0.12)) @@ -118,7 +118,7 @@ struct DataGapsSheet: View { Text(rangeLabel(gap)) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) if let source = sourceProvider(gap.sourceId), let firstMissing = gap.missingMonthDates.first { diff --git a/PortfolioJournal/Views/Dashboard/EvolutionChart.swift b/PortfolioJournal/Views/Dashboard/EvolutionChart.swift index d539cf9..d52d3a9 100644 --- a/PortfolioJournal/Views/Dashboard/EvolutionChart.swift +++ b/PortfolioJournal/Views/Dashboard/EvolutionChart.swift @@ -147,7 +147,7 @@ struct EvolutionChartCard: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) .contentShape(Rectangle()) // simultaneousGesture so the swipe is recognized even over the chart, @@ -206,16 +206,16 @@ struct EvolutionChartCard: View { .frame(width: 72, height: 72) Image(systemName: page.chartsType.icon) .font(.system(size: 28)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) Image(systemName: "lock.circle.fill") .font(.system(size: 22)) - .foregroundColor(.appWarning) + .foregroundStyle(Color.appWarning) .background(Circle().fill(Color(.systemBackground))) .offset(x: 26, y: 24) } Text(String(localized: "home_chart_locked_sub")) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) Button { FirebaseService.shared.logPaywallShown(trigger: "home_charts") @@ -226,7 +226,7 @@ struct EvolutionChartCard: View { .padding(.horizontal, 20) .padding(.vertical, 10) .background(Color.appPrimary) - .foregroundColor(.white) + .foregroundStyle(.white) .clipShape(Capsule()) } } @@ -246,7 +246,7 @@ struct EvolutionChartCard: View { showGoalLines.toggle() } label: { Image(systemName: showGoalLines ? "target" : "slash.circle") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .accessibilityLabel(showGoalLines ? "Hide goals" : "Show goals") @@ -256,7 +256,7 @@ struct EvolutionChartCard: View { .font(.subheadline.weight(.semibold)) Text(selected.date.monthYearString) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -303,7 +303,7 @@ struct EvolutionChartCard: View { Text(item.category).font(.caption) Spacer() Text(String(format: "%.1f%%", pct)).font(.caption.weight(.medium)) - Text(item.value.compactCurrencyString).font(.caption).foregroundColor(.secondary) + Text(item.value.compactCurrencyString).font(.caption).foregroundStyle(.secondary) } } } @@ -325,7 +325,7 @@ struct EvolutionChartCard: View { } else { Text("Not enough data to display chart") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .frame(height: 200) .frame(maxWidth: .infinity) } @@ -449,7 +449,7 @@ struct EvolutionChartCard: View { .annotation(position: .topTrailing) { Text(goal.name) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } diff --git a/PortfolioJournal/Views/Dashboard/GuidedCheckInView.swift b/PortfolioJournal/Views/Dashboard/GuidedCheckInView.swift index 1996c1b..67c6120 100644 --- a/PortfolioJournal/Views/Dashboard/GuidedCheckInView.swift +++ b/PortfolioJournal/Views/Dashboard/GuidedCheckInView.swift @@ -126,7 +126,7 @@ struct GuidedCheckInView: View { if let category = source.category?.name { Text(category) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -134,10 +134,10 @@ struct GuidedCheckInView: View { VStack(spacing: 2) { Text(String(localized: "guided_previous_value")) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(source.latestValue.currencyString) .font(.title3.weight(.semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -150,7 +150,7 @@ struct GuidedCheckInView: View { .padding(.vertical, 12) .padding(.horizontal, 24) .background(Color(.secondarySystemGroupedBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .padding(.horizontal, 32) liveDelta(for: source) @@ -202,7 +202,7 @@ struct GuidedCheckInView: View { } label: { Text(String(localized: "guided_skip")) .font(.subheadline.weight(.medium)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .accessibilityIdentifier("guided_skip_btn") } @@ -224,7 +224,7 @@ struct GuidedCheckInView: View { Text("\(delta.currencyString) (\(String(format: "%+.1f%%", pct)))") .font(.subheadline.weight(.semibold)) } - .foregroundColor(positive ? .positiveGreen : .negativeRed) + .foregroundStyle(positive ? Color.positiveGreen : Color.negativeRed) .padding(.horizontal, 12) .padding(.vertical, 6) .background((positive ? Color.positiveGreen : Color.negativeRed).opacity(0.12)) @@ -247,7 +247,7 @@ struct GuidedCheckInView: View { .font(.title2.weight(.bold)) Text(String(localized: "guided_reflection_subtitle")) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } HStack(spacing: 10) { @@ -272,8 +272,8 @@ struct GuidedCheckInView: View { ? Color.appPrimary.opacity(0.15) : Color(.secondarySystemGroupedBackground) ) - .foregroundColor(mood == candidate ? .appPrimary : .primary) - .cornerRadius(12) + .foregroundStyle(mood == candidate ? Color.appPrimary : .primary) + .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay( RoundedRectangle(cornerRadius: 12) .strokeBorder(mood == candidate ? Color.appPrimary : .clear, lineWidth: 1.5) @@ -290,7 +290,7 @@ struct GuidedCheckInView: View { .focused($noteFocused) .padding(14) .background(Color(.secondarySystemGroupedBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .padding(.horizontal, 24) Spacer() @@ -319,7 +319,7 @@ struct GuidedCheckInView: View { Image(systemName: "checkmark.seal.fill") .font(.system(size: 64)) - .foregroundColor(.positiveGreen) + .foregroundStyle(Color.positiveGreen) .symbolEffect(.bounce, value: step) Text(String(format: String(localized: "guided_done_title"), monthLabel)) @@ -335,7 +335,7 @@ struct GuidedCheckInView: View { let pct = NSDecimalNumber(decimal: delta / totals.previous).doubleValue * 100 Text("\(delta.currencyString) (\(String(format: "%+.1f%%", pct)))") .font(.subheadline.weight(.semibold)) - .foregroundColor(delta >= 0 ? .positiveGreen : .negativeRed) + .foregroundStyle(delta >= 0 ? Color.positiveGreen : Color.negativeRed) } } } @@ -347,7 +347,7 @@ struct GuidedCheckInView: View { Text(String(format: String(localized: "guided_streak"), streak)) } .font(.subheadline.weight(.semibold)) - .foregroundColor(.orange) + .foregroundStyle(.orange) .padding(.horizontal, 14) .padding(.vertical, 8) .background(Color.orange.opacity(0.12)) diff --git a/PortfolioJournal/Views/Dashboard/InsightsRow.swift b/PortfolioJournal/Views/Dashboard/InsightsRow.swift index b9d6753..971831a 100644 --- a/PortfolioJournal/Views/Dashboard/InsightsRow.swift +++ b/PortfolioJournal/Views/Dashboard/InsightsRow.swift @@ -21,7 +21,7 @@ struct InsightsRow: View { HStack(spacing: 12) { Image(systemName: insight.systemImage) .font(.subheadline.weight(.semibold)) - .foregroundColor(insight.accentColor) + .foregroundStyle(insight.accentColor) .frame(width: 30, height: 30) .background(insight.accentColor.opacity(0.12)) .clipShape(Circle()) @@ -29,15 +29,15 @@ struct InsightsRow: View { VStack(alignment: .leading, spacing: 1) { Text(insight.title) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(insight.value) .font(.subheadline.weight(.semibold)) - .foregroundColor(.primary) + .foregroundStyle(.primary) .lineLimit(2) if let detail = insight.detail { Text(detail) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .lineLimit(1) } } @@ -56,7 +56,7 @@ struct InsightsRow: View { } .padding(14) .background(Color(.secondarySystemGroupedBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } .buttonStyle(.plain) .id(insight.id) diff --git a/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift b/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift index 9f5693f..bd9ecc3 100644 --- a/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift +++ b/PortfolioJournal/Views/Dashboard/MonthlyCheckInView.swift @@ -116,7 +116,7 @@ struct MonthlyCheckInView: View { } label: { Image(systemName: "chevron.left") .font(.subheadline.weight(.semibold)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } Text(monthLabel) @@ -127,13 +127,13 @@ struct MonthlyCheckInView: View { } label: { Image(systemName: "chevron.right") .font(.subheadline.weight(.semibold)) - .foregroundColor(canGoToNextMonth ? .appPrimary : .secondary.opacity(0.3)) + .foregroundStyle(canGoToNextMonth ? Color.appPrimary : .secondary.opacity(0.3)) } .disabled(!canGoToNextMonth) } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button { shareMonthlyCheckIn() } label: { @@ -230,11 +230,11 @@ struct MonthlyCheckInView: View { ) ) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } else { Text("Start your first check-in anytime.") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -242,10 +242,10 @@ struct MonthlyCheckInView: View { if let completed = MonthlyCheckInStore.completionDate(for: referenceDate) { HStack(spacing: 6) { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.positiveGreen) + .foregroundStyle(Color.positiveGreen) Text("Completed \(completed.friendlyDescription)") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -272,12 +272,12 @@ struct MonthlyCheckInView: View { .frame(maxWidth: .infinity) .padding(.vertical, 10) .background(isCompleted ? Color.appSecondary.opacity(0.1) : Color.appPrimary.opacity(0.1)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -310,7 +310,7 @@ struct MonthlyCheckInView: View { Spacer() Text("Optional") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } VStack(alignment: .leading, spacing: 8) { @@ -326,7 +326,7 @@ struct MonthlyCheckInView: View { } label: { Image(systemName: value <= starRating ? "star.fill" : "star") .font(.title3) - .foregroundColor(value <= starRating ? .appSecondary : .secondary) + .foregroundStyle(value <= starRating ? Color.appSecondary : .secondary) .padding(8) .background( Circle() @@ -346,7 +346,7 @@ struct MonthlyCheckInView: View { .padding(.horizontal, 10) .padding(.vertical, 6) .background(Color.gray.opacity(0.12)) - .cornerRadius(AppConstants.UI.smallCornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)) } } } @@ -373,7 +373,7 @@ struct MonthlyCheckInView: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -386,7 +386,7 @@ struct MonthlyCheckInView: View { VStack(alignment: .leading, spacing: 4) { Text("Starting") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(viewModel.monthlySummary.formattedStartingValue) .font(.subheadline.weight(.semibold)) } @@ -394,7 +394,7 @@ struct MonthlyCheckInView: View { VStack(alignment: .trailing, spacing: 4) { Text("Ending") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(viewModel.monthlySummary.formattedEndingValue) .font(.subheadline.weight(.semibold)) } @@ -404,7 +404,7 @@ struct MonthlyCheckInView: View { VStack(alignment: .leading, spacing: 4) { Text("Contributions") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(viewModel.monthlySummary.formattedContributions) .font(.subheadline.weight(.semibold)) } @@ -412,16 +412,16 @@ struct MonthlyCheckInView: View { VStack(alignment: .trailing, spacing: 4) { Text("Net Performance") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("\(viewModel.monthlySummary.formattedNetPerformance) (\(viewModel.monthlySummary.formattedNetPerformancePercentage))") .font(.subheadline.weight(.semibold)) - .foregroundColor(viewModel.monthlySummary.netPerformance >= 0 ? .positiveGreen : .negativeRed) + .foregroundStyle(viewModel.monthlySummary.netPerformance >= 0 ? Color.positiveGreen : Color.negativeRed) } } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -489,7 +489,7 @@ struct MonthlyCheckInView: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } } @@ -497,11 +497,11 @@ struct MonthlyCheckInView: View { private func highlightRow(icon: String, iconColor: Color, label: String, name: String, percentage: Double, diff: Decimal, valueColor: Color) -> some View { HStack { Image(systemName: icon) - .foregroundColor(iconColor) + .foregroundStyle(iconColor) VStack(alignment: .leading, spacing: 2) { Text(label) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(name) .font(.subheadline.weight(.semibold)) } @@ -509,10 +509,10 @@ struct MonthlyCheckInView: View { VStack(alignment: .trailing, spacing: 2) { Text(String(format: "%+.1f%%", percentage)) .font(.subheadline.weight(.bold)) - .foregroundColor(valueColor) + .foregroundStyle(valueColor) Text("(\(diff.compactCurrencyString))") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -529,18 +529,18 @@ struct MonthlyCheckInView: View { } label: { Label("Batch Update", systemImage: "square.and.pencil") .font(.caption.weight(.semibold)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } } Text("\(viewModel.sources.count)") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } if viewModel.sources.isEmpty { Text("Add sources to start your monthly check-in.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } else { ForEach(viewModel.sources, id: \.objectID) { source in // Month-aware: "updated" means updated FOR THE VIEWED month. @@ -575,7 +575,7 @@ struct MonthlyCheckInView: View { .font(.subheadline.weight(.medium)) Text(updatedThisCycle ? "Updated this cycle" : "Needs update") .font(.caption2) - .foregroundColor(updatedThisCycle ? .positiveGreen : .secondary) + .foregroundStyle(updatedThisCycle ? Color.positiveGreen : .secondary) } Spacer() @@ -583,16 +583,16 @@ struct MonthlyCheckInView: View { if let diff = valueDiff, updatedThisCycle { Text(diff >= 0 ? "+\(diff.compactCurrencyString)" : diff.compactCurrencyString) .font(.caption.weight(.semibold)) - .foregroundColor(diff >= 0 ? .positiveGreen : .negativeRed) + .foregroundStyle(diff >= 0 ? Color.positiveGreen : Color.negativeRed) } Text(latestSnapshot?.date.relativeDayDescription ?? String(localized: "date_never")) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Image(systemName: "chevron.right") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } .buttonStyle(.plain) @@ -601,7 +601,7 @@ struct MonthlyCheckInView: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -614,7 +614,7 @@ struct MonthlyCheckInView: View { .frame(minHeight: 120) .padding(8) .background(Color.gray.opacity(0.08)) - .cornerRadius(12) + .clipShape(RoundedRectangle(cornerRadius: 12)) .focused($noteFocused) .onChange(of: monthlyNote) { _, newValue in MonthlyCheckInStore.setNote(newValue, for: referenceDate) @@ -629,7 +629,7 @@ struct MonthlyCheckInView: View { .frame(maxWidth: .infinity) .padding(.vertical, 8) .background(Color.appSecondary.opacity(0.12)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } .buttonStyle(.plain) @@ -642,13 +642,13 @@ struct MonthlyCheckInView: View { .frame(maxWidth: .infinity) .padding(.vertical, 8) .background(Color.appPrimary.opacity(0.12)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -660,7 +660,7 @@ struct MonthlyCheckInView: View { if viewModel.recentNotes.isEmpty { Text("No snapshot notes for this month.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } else { ForEach(viewModel.recentNotes, id: \.objectID) { snapshot in VStack(alignment: .leading, spacing: 4) { @@ -668,10 +668,10 @@ struct MonthlyCheckInView: View { .font(.subheadline.weight(.semibold)) Text(snapshot.notes ?? "") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(snapshot.date.friendlyDescription) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } if snapshot.id != viewModel.recentNotes.last?.id { @@ -682,7 +682,7 @@ struct MonthlyCheckInView: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -764,11 +764,11 @@ struct AchievementsView: View { ) ) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -785,7 +785,7 @@ struct AchievementsView: View { if let subtitle { Text(subtitle) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } if achievements.isEmpty { @@ -798,7 +798,7 @@ struct AchievementsView: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -810,7 +810,7 @@ struct AchievementsView: View { .frame(width: 42, height: 42) Image(systemName: status.achievement.icon) .font(.headline) - .foregroundColor(isLocked ? .secondary : .appSecondary) + .foregroundStyle(isLocked ? .secondary : Color.appSecondary) } VStack(alignment: .leading, spacing: 2) { @@ -818,7 +818,7 @@ struct AchievementsView: View { .font(.subheadline.weight(.semibold)) Text(status.achievement.detail) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() @@ -826,12 +826,12 @@ struct AchievementsView: View { if isLocked { Image(systemName: "lock.fill") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } .padding(10) .background(isLocked ? Color.gray.opacity(0.08) : Color.appSecondary.opacity(0.12)) - .cornerRadius(AppConstants.UI.smallCornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)) } } @@ -865,13 +865,13 @@ private extension MonthlyCheckInView { HStack(alignment: .center, spacing: 8) { Image(systemName: mood.iconName) .font(.body) - .foregroundColor(isSelected ? moodColor(for: mood) : .secondary) + .foregroundStyle(isSelected ? moodColor(for: mood) : .secondary) VStack(alignment: .leading, spacing: 2) { Text(mood.title) .font(.subheadline.weight(.semibold)) Text(mood.detail) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } .padding(10) @@ -880,7 +880,7 @@ private extension MonthlyCheckInView { RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius) .stroke(isSelected ? moodColor(for: mood) : Color.clear, lineWidth: 1) ) - .cornerRadius(AppConstants.UI.smallCornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)) } func moodColor(for mood: MonthlyCheckInMood) -> Color { @@ -940,7 +940,7 @@ struct AchievementMilestoneBar: View { if status.isUnlocked { Image(systemName: "checkmark") .font(.system(size: 8, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } } .position(x: x, y: barY) @@ -989,10 +989,10 @@ struct BatchUpdateView: View { .navigationTitle("Batch Update") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Save") { saveAll() } .disabled(filledCount == 0) .fontWeight(.semibold) @@ -1030,33 +1030,33 @@ struct BatchUpdateView: View { if let prev = previousValue { Text(prev.currencyString) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } HStack { Text(symbol) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) TextField("Current value", text: valueBinding) .keyboardType(.decimalPad) } .padding(8) .background(Color.gray.opacity(0.08)) - .cornerRadius(8) + .clipShape(RoundedRectangle(cornerRadius: 8)) if isDetailed { HStack { Image(systemName: "plus.circle") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) TextField("Contribution this period (optional)", text: contributionBinding) .keyboardType(.decimalPad) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding(8) .background(Color.appSecondary.opacity(0.06)) - .cornerRadius(8) + .clipShape(RoundedRectangle(cornerRadius: 8)) } } .padding(.vertical, 2) diff --git a/PortfolioJournal/Views/Dashboard/MonthlySummaryShareView.swift b/PortfolioJournal/Views/Dashboard/MonthlySummaryShareView.swift index 9fa6673..ecb75d0 100644 --- a/PortfolioJournal/Views/Dashboard/MonthlySummaryShareView.swift +++ b/PortfolioJournal/Views/Dashboard/MonthlySummaryShareView.swift @@ -21,7 +21,7 @@ struct MonthlySummaryShareView: View { HStack { Text(monthLabel) .font(.caption.weight(.semibold)) - .foregroundColor(.white.opacity(0.85)) + .foregroundStyle(.white.opacity(0.85)) .textCase(.uppercase) Spacer() if streak >= 2 { @@ -31,7 +31,7 @@ struct MonthlySummaryShareView: View { Text(String(format: String(localized: "share_summary_streak_badge"), streak)) .font(.caption2.weight(.bold)) } - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.horizontal, 10) .padding(.vertical, 5) .background(Color.white.opacity(0.18)) @@ -41,17 +41,17 @@ struct MonthlySummaryShareView: View { Text("Monthly Summary") .font(.title.weight(.bold)) - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.top, 6) // Hero value VStack(alignment: .leading, spacing: 6) { Text("Portfolio Value") .font(.subheadline.weight(.semibold)) - .foregroundColor(.white.opacity(0.85)) + .foregroundStyle(.white.opacity(0.85)) Text(totalValue) .font(.system(size: 40, weight: .bold, design: .rounded)) - .foregroundColor(.white) + .foregroundStyle(.white) .minimumScaleFactor(0.6) .lineLimit(1) } @@ -66,9 +66,9 @@ struct MonthlySummaryShareView: View { Spacer() Text("since last check-in") .font(.caption) - .foregroundColor(.white.opacity(0.75)) + .foregroundStyle(.white.opacity(0.75)) } - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.horizontal, 14) .padding(.vertical, 10) .background(Color.white.opacity(0.15)) @@ -85,14 +85,14 @@ struct MonthlySummaryShareView: View { Text(mood.title) .font(.subheadline.weight(.semibold)) } - .foregroundColor(.white) + .foregroundStyle(.white) } if let rating { HStack(spacing: 2) { ForEach(1...5, id: \.self) { i in Image(systemName: i <= rating ? "star.fill" : "star") .font(.caption) - .foregroundColor(.white.opacity(i <= rating ? 1 : 0.4)) + .foregroundStyle(.white.opacity(i <= rating ? 1 : 0.4)) } } } @@ -114,10 +114,10 @@ struct MonthlySummaryShareView: View { VStack(alignment: .leading, spacing: 2) { Text("Tracked with") .font(.caption2.weight(.medium)) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) Text(appName) .font(.subheadline.weight(.bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } Spacer() diff --git a/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift b/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift index 8af73da..fd59768 100644 --- a/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift +++ b/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift @@ -91,7 +91,7 @@ struct QuickUpdateView: View { Image(systemName: "arrow.down.circle.fill") } .font(.subheadline.weight(.semibold)) - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.vertical, 2) } .listRowBackground(Color.appPrimary) @@ -243,14 +243,14 @@ struct QuickUpdateView: View { .padding(.horizontal, 6) .padding(.vertical, 1) .background(Color.appSecondary.opacity(0.15)) - .foregroundColor(.appSecondary) + .foregroundStyle(Color.appSecondary) .clipShape(Capsule()) } } if source.latestValue != .zero { Text(source.latestValue.currencyString) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } Spacer() @@ -269,7 +269,7 @@ struct QuickUpdateView: View { HStack { Text(String(localized: "quick_update_contribution_label")) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() TextField( String(localized: "quick_update_contribution_placeholder"), @@ -279,7 +279,7 @@ struct QuickUpdateView: View { .multilineTextAlignment(.trailing) .frame(width: 120) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } diff --git a/PortfolioJournal/Views/Dashboard/ShareCards.swift b/PortfolioJournal/Views/Dashboard/ShareCards.swift index 3ef3e60..388bbdb 100644 --- a/PortfolioJournal/Views/Dashboard/ShareCards.swift +++ b/PortfolioJournal/Views/Dashboard/ShareCards.swift @@ -157,7 +157,7 @@ private func eyebrow(_ text: String) -> some View { Text(text.uppercased()) .font(.caption2.weight(.bold)) .tracking(2.2) - .foregroundColor(ShareCardStyle.green) + .foregroundStyle(ShareCardStyle.green) } private func deltaChip(_ text: String, positive: Bool, large: Bool = false) -> some View { @@ -167,7 +167,7 @@ private func deltaChip(_ text: String, positive: Bool, large: Bool = false) -> s Text(text) .font((large ? Font.headline : Font.caption).weight(.bold)) } - .foregroundColor(positive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) + .foregroundStyle(positive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) .padding(.horizontal, large ? 12 : 9) .padding(.vertical, large ? 6 : 4) .background( @@ -179,11 +179,11 @@ private func metricRow(_ title: String, _ value: String, theme: ShareCardTheme) HStack { Text(title) .font(.caption.weight(.semibold)) - .foregroundColor(theme.mutedInk) + .foregroundStyle(theme.mutedInk) Spacer() Text(value) .font(.subheadline.weight(.semibold)) - .foregroundColor(theme.ink) + .foregroundStyle(theme.ink) .multilineTextAlignment(.trailing) .minimumScaleFactor(0.7) .lineLimit(1) @@ -203,13 +203,13 @@ private func brandHeader(theme: ShareCardTheme, asOf: String?) -> some View { VStack(alignment: .leading, spacing: 1) { Text("Portfolio Journal") .font(.system(.title2, design: .rounded).weight(.heavy)) - .foregroundColor(theme.ink) + .foregroundStyle(theme.ink) .minimumScaleFactor(0.7) .lineLimit(1) if let asOf { Text(String(format: String(localized: "share_as_of"), asOf)) .font(.caption2.weight(.medium)) - .foregroundColor(theme.mutedInk) + .foregroundStyle(theme.mutedInk) } } Spacer(minLength: 0) @@ -223,11 +223,11 @@ private func downloadFooter(qrCodeImage: UIImage?, theme: ShareCardTheme) -> som VStack(alignment: .leading, spacing: 3) { Text(String(localized: "share_cta_headline")) .font(.subheadline.weight(.bold)) - .foregroundColor(theme.ink) + .foregroundStyle(theme.ink) .fixedSize(horizontal: false, vertical: true) Text(String(localized: "share_cta_sub")) .font(.caption2.weight(.medium)) - .foregroundColor(theme.mutedInk) + .foregroundStyle(theme.mutedInk) } Spacer(minLength: 8) @@ -295,13 +295,13 @@ struct PortfolioValueShareCardView: View { eyebrow(String(localized: "share_hero_eyebrow")) Text(heroPercent ?? totalValue) .font(.system(size: storyFormat ? 52 : 44, weight: .heavy, design: .rounded)) - .foregroundColor(heroIsPercent ? (isPositive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) : theme.ink) + .foregroundStyle(heroIsPercent ? (isPositive ? ShareCardStyle.green : Color(red: 1, green: 0.42, blue: 0.42)) : theme.ink) .minimumScaleFactor(0.5) .lineLimit(1) if heroIsPercent && !totalValue.isEmpty { Text(totalValue) .font(.title3.weight(.bold)) - .foregroundColor(theme.ink) + .foregroundStyle(theme.ink) .minimumScaleFactor(0.5) .lineLimit(1) } @@ -363,7 +363,7 @@ struct MonthlyCheckInShareCardView: View { eyebrow(summary.formattedMonthYear) Text("Monthly Check-in") .font(.title.weight(.bold)) - .foregroundColor(theme.ink) + .foregroundStyle(theme.ink) .padding(.top, 4) VStack(spacing: 9) { @@ -472,8 +472,8 @@ struct PortfolioShareOptionsView: View { .frame(maxWidth: .infinity) .padding(.vertical, 14) .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(14) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 14)) } .padding(.horizontal) .padding(.bottom, 8) diff --git a/PortfolioJournal/Views/Goals/GoalEditorView.swift b/PortfolioJournal/Views/Goals/GoalEditorView.swift index 34b4bfd..10d1552 100644 --- a/PortfolioJournal/Views/Goals/GoalEditorView.swift +++ b/PortfolioJournal/Views/Goals/GoalEditorView.swift @@ -41,7 +41,7 @@ struct GoalEditorView: View { TextField("Goal name", text: $name) HStack { Text(currencySymbol) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) TextField("Target amount", text: $targetAmount) .keyboardType(.decimalPad) } @@ -73,10 +73,10 @@ struct GoalEditorView: View { } .navigationTitle(goal == nil ? "New Goal" : "Edit Goal") .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Save") { saveGoal() } .disabled(!isValid) } @@ -86,7 +86,7 @@ struct GoalEditorView: View { .presentationDragIndicator(.visible) .onAppear { guard let goal, !didLoadGoal else { return } - name = goal.name ?? "" + name = goal.name if let amount = goal.targetAmount?.decimalValue { targetAmount = formatDecimalForInput(amount) } diff --git a/PortfolioJournal/Views/Goals/GoalProgressBar.swift b/PortfolioJournal/Views/Goals/GoalProgressBar.swift index 0ac2c0b..4aae724 100644 --- a/PortfolioJournal/Views/Goals/GoalProgressBar.swift +++ b/PortfolioJournal/Views/Goals/GoalProgressBar.swift @@ -26,7 +26,7 @@ struct GoalProgressBar: View { .overlay(alignment: .leading) { Image(systemName: iconName) .font(.caption2) - .foregroundColor(iconColor) + .foregroundStyle(iconColor) .offset(x: iconOffset) } } diff --git a/PortfolioJournal/Views/Goals/GoalShareCardView.swift b/PortfolioJournal/Views/Goals/GoalShareCardView.swift index 02c1426..81539af 100644 --- a/PortfolioJournal/Views/Goals/GoalShareCardView.swift +++ b/PortfolioJournal/Views/Goals/GoalShareCardView.swift @@ -34,10 +34,10 @@ struct GoalShareCardView: View { VStack(alignment: .leading, spacing: 4) { Text("Goal Progress") .font(.caption.weight(.semibold)) - .foregroundColor(.white.opacity(0.8)) + .foregroundStyle(.white.opacity(0.8)) Text(name) .font(.title2.weight(.bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } GoalProgressBar( @@ -51,27 +51,27 @@ struct GoalShareCardView: View { Text("\(progressText) complete") .font(.caption.weight(.semibold)) - .foregroundColor(.white.opacity(0.85)) + .foregroundStyle(.white.opacity(0.85)) if privacyMode { HStack { Text("Progress") .font(.subheadline.weight(.semibold)) - .foregroundColor(.white.opacity(0.9)) + .foregroundStyle(.white.opacity(0.9)) Spacer() Text(progressText) .font(.headline.weight(.bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } } else { HStack { Text(displayCurrentValue) .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) Spacer() Text("of \(targetValue.currencyString)") .font(.subheadline.weight(.medium)) - .foregroundColor(.white.opacity(0.8)) + .foregroundStyle(.white.opacity(0.8)) } } @@ -82,7 +82,7 @@ struct GoalShareCardView: View { Text("Target: \(targetDate.mediumDateString)") .font(.caption.weight(.medium)) } - .foregroundColor(.white.opacity(0.85)) + .foregroundStyle(.white.opacity(0.85)) } if let estimatedCompletionDate { @@ -92,7 +92,7 @@ struct GoalShareCardView: View { Text("Est. completion: \(estimatedCompletionDate.mediumDateString)") .font(.caption.weight(.medium)) } - .foregroundColor(.white.opacity(0.85)) + .foregroundStyle(.white.opacity(0.85)) } if privacyMode { @@ -101,7 +101,7 @@ struct GoalShareCardView: View { Text("Privacy mode enabled") } .font(.caption.weight(.semibold)) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) } Spacer(minLength: 0) @@ -124,10 +124,10 @@ struct GoalShareCardView: View { VStack(alignment: .leading, spacing: 2) { Text("Powered by") .font(.caption2.weight(.medium)) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) Text("Portfolio Journal") .font(.subheadline.weight(.bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } Spacer() @@ -144,7 +144,7 @@ struct GoalShareCardView: View { Text("Scan to download") .font(.system(size: 7, weight: .medium)) - .foregroundColor(.white.opacity(0.8)) + .foregroundStyle(.white.opacity(0.8)) } } else { // Fallback if QR code generation fails @@ -154,7 +154,7 @@ struct GoalShareCardView: View { Text("App Store") .font(.caption2.weight(.semibold)) } - .foregroundColor(.white.opacity(0.9)) + .foregroundStyle(.white.opacity(0.9)) } } } diff --git a/PortfolioJournal/Views/Goals/GoalsView.swift b/PortfolioJournal/Views/Goals/GoalsView.swift index ee646ca..9c81c0d 100644 --- a/PortfolioJournal/Views/Goals/GoalsView.swift +++ b/PortfolioJournal/Views/Goals/GoalsView.swift @@ -72,7 +72,7 @@ struct GoalsView: View { } .navigationTitle("Goals") .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Picker(String(localized: "goals_filter_active"), selection: $goalFilter) { ForEach(GoalFilter.allCases) { filter in Text(filter.label).tag(filter) @@ -80,7 +80,7 @@ struct GoalsView: View { } .pickerStyle(.menu) } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button { showingAddGoal = true } label: { @@ -145,19 +145,19 @@ struct GoalsView: View { VStack(spacing: 16) { Image(systemName: "target") .font(.system(size: 48)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("Set your first goal") .font(.headline) Text("Track progress toward milestones like \(AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currencySymbol)1M and share your wins.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) Button { showingAddGoal = true } label: { Text(String(localized: "goals_empty_add_cta")) .font(.subheadline.weight(.semibold)) - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.horizontal, 20) .padding(.vertical, 10) .background(Color.appPrimary) @@ -170,13 +170,13 @@ struct GoalsView: View { VStack(spacing: 12) { Image(systemName: "archivebox") .font(.system(size: 40)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(String(localized: "goals_all_active_achieved")) .font(.headline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("Switch to \"Archived\" or \"All\" to see other goals.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) @@ -186,10 +186,10 @@ struct GoalsView: View { VStack(spacing: 12) { Image(systemName: "archivebox") .font(.system(size: 40)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(String(localized: "goals_empty_archived")) .font(.headline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity) .padding(.vertical, 32) @@ -197,12 +197,12 @@ struct GoalsView: View { VStack(spacing: 16) { Image(systemName: "target") .font(.system(size: 48)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("Set your first goal") .font(.headline) Text("Track progress toward milestones like \(AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currencySymbol)1M and share your wins.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) @@ -265,7 +265,7 @@ struct GoalRowView: View { if isAchieved { Text("Achieved") .font(.caption2.weight(.bold)) - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.horizontal, 8) .padding(.vertical, 3) .background(Color.appSuccess) @@ -280,7 +280,7 @@ struct GoalRowView: View { Text(category.name) .font(.caption2.weight(.semibold)) } - .foregroundColor(category.color) + .foregroundStyle(category.color) .padding(.horizontal, 8) .padding(.vertical, 3) .background(category.color.opacity(0.15)) @@ -293,21 +293,21 @@ struct GoalRowView: View { .hiddenBalance() Text("of \(goal.targetDecimal.currencyString)") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .hiddenBalance() } if let targetDate = goal.targetDate { Text("Target date: \(targetDate.mediumDateString)") .font(.caption) - .foregroundColor(targetDateColor) + .foregroundStyle(targetDateColor) } if let paceStatus { Text(paceStatus.statusText) .font(.caption.weight(.semibold)) - .foregroundColor( - isAchieved ? .appSuccess : (paceStatus.isBehind ? .appWarning : .positiveGreen) + .foregroundStyle( + isAchieved ? Color.appSuccess : (paceStatus.isBehind ? Color.appWarning : Color.positiveGreen) ) } @@ -326,7 +326,7 @@ struct GoalRowView: View { showingShareOptions = true } label: { Image(systemName: "square.and.arrow.up") - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) .padding(.top, 2) } .buttonStyle(.borderless) @@ -366,13 +366,13 @@ struct GoalRowView: View { case .unreachable: Label(String(localized: "goal_projection_add_contributions"), systemImage: "plus.circle") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) default: HStack(spacing: 8) { if let date = projection.projectedDate { Text(String(format: String(localized: "goal_projection_projected"), date.monthYearString)) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } onTrackChip Spacer(minLength: 0) @@ -409,7 +409,7 @@ struct GoalRowView: View { if projection.status != .unreachable { Text(chipText) .font(.caption2.weight(.semibold)) - .foregroundColor(chipColor) + .foregroundStyle(chipColor) .padding(.horizontal, 8) .padding(.vertical, 3) .background(chipColor.opacity(0.15)) diff --git a/PortfolioJournal/Views/Onboarding/OnboardingICloudCheckView.swift b/PortfolioJournal/Views/Onboarding/OnboardingICloudCheckView.swift index 6e8ef43..d74d548 100644 --- a/PortfolioJournal/Views/Onboarding/OnboardingICloudCheckView.swift +++ b/PortfolioJournal/Views/Onboarding/OnboardingICloudCheckView.swift @@ -33,7 +33,7 @@ struct OnboardingICloudCheckView: View { .frame(width: 100, height: 100) Image(systemName: "icloud.fill") .font(.system(size: 50)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } VStack(spacing: 14) { @@ -43,7 +43,7 @@ struct OnboardingICloudCheckView: View { Text(String(localized: "icloud_check_description")) .font(.body) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) } @@ -59,11 +59,11 @@ struct OnboardingICloudCheckView: View { } label: { Label("Restore from iCloud", systemImage: "icloud.and.arrow.down") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } Button { @@ -71,7 +71,7 @@ struct OnboardingICloudCheckView: View { } label: { Text("Start Fresh") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } .padding(.horizontal, 24) @@ -96,7 +96,7 @@ struct OnboardingICloudCheckView: View { .frame(width: 100, height: 100) Image(systemName: "checkmark.icloud.fill") .font(.system(size: 50)) - .foregroundColor(.positiveGreen) + .foregroundStyle(Color.positiveGreen) } VStack(spacing: 14) { @@ -106,7 +106,7 @@ struct OnboardingICloudCheckView: View { Text(String(localized: "icloud_enabled_description")) .font(.body) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) } @@ -123,11 +123,11 @@ struct OnboardingICloudCheckView: View { } label: { Text("Got it") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() .background(Color.positiveGreen) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } .padding(.horizontal, 24) .padding(.bottom, 40) diff --git a/PortfolioJournal/Views/Onboarding/OnboardingView.swift b/PortfolioJournal/Views/Onboarding/OnboardingView.swift index 370ce08..abdcdd9 100644 --- a/PortfolioJournal/Views/Onboarding/OnboardingView.swift +++ b/PortfolioJournal/Views/Onboarding/OnboardingView.swift @@ -88,11 +88,11 @@ struct OnboardingView: View { } label: { Text("Continue") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } Button { @@ -101,7 +101,7 @@ struct OnboardingView: View { } label: { Text("Skip") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } else { Button { @@ -109,11 +109,11 @@ struct OnboardingView: View { } label: { Text("Get Started") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } } @@ -144,7 +144,7 @@ struct OnboardingView: View { categoryRepository.createDefaultCategoriesIfNeeded() // Create default account if needed - AccountRepository().createDefaultAccountIfNeeded() + _ = AccountRepository().createDefaultAccountIfNeeded() // Mark onboarding as complete let context = CoreDataStack.shared.viewContext @@ -201,7 +201,7 @@ struct OnboardingPageView: View { Image(systemName: page.icon) .font(.system(size: 50)) - .foregroundColor(page.color) + .foregroundStyle(page.color) } // Content @@ -212,7 +212,7 @@ struct OnboardingPageView: View { Text(page.description) .font(.body) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) } @@ -255,7 +255,7 @@ struct OnboardingQuickStartView: View { Text(String(localized: "onboarding_quickstart_subtitle")) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 30) } @@ -268,8 +268,8 @@ struct OnboardingQuickStartView: View { .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(AppConstants.UI.cornerRadius) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } Button(action: onImport) { @@ -278,8 +278,8 @@ struct OnboardingQuickStartView: View { .frame(maxWidth: .infinity) .padding(.vertical, 12) .background(Color.appPrimary.opacity(0.1)) - .foregroundColor(.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .foregroundStyle(Color.appPrimary) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } @@ -297,13 +297,13 @@ struct OnboardingQuickStartView: View { } .padding() .background(Color.gray.opacity(0.1)) - .cornerRadius(12) + .clipShape(RoundedRectangle(cornerRadius: 12)) Toggle("Sync with iCloud (optional)", isOn: $cloudSyncEnabled) .padding() .background(Color.gray.opacity(0.1)) - .cornerRadius(12) + .clipShape(RoundedRectangle(cornerRadius: 12)) } Spacer().frame(height: 8) @@ -326,7 +326,7 @@ struct WelcomeView: View { Image(systemName: "hand.wave.fill") .font(.system(size: 60)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) VStack(spacing: 8) { if let name = userName { @@ -341,7 +341,7 @@ struct WelcomeView: View { ? "Your investment data has been synced from iCloud." : "Your investment data stays on this device.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } @@ -354,11 +354,11 @@ struct WelcomeView: View { } label: { Text("Continue") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } .padding(.horizontal, 24) .padding(.bottom, 40) diff --git a/PortfolioJournal/Views/Premium/PaywallView.swift b/PortfolioJournal/Views/Premium/PaywallView.swift index 0e6adee..e347e4a 100644 --- a/PortfolioJournal/Views/Premium/PaywallView.swift +++ b/PortfolioJournal/Views/Premium/PaywallView.swift @@ -16,11 +16,11 @@ private struct PremiumChartPreview: View { HStack(alignment: .firstTextBaseline) { Text("€24,750") .font(.system(size: 18, weight: .bold, design: .rounded)) - .foregroundColor(.primary) + .foregroundStyle(.primary) Spacer() Text("+34.2%") .font(.caption.weight(.semibold)) - .foregroundColor(.positiveGreen) + .foregroundStyle(Color.positiveGreen) .padding(.horizontal, 7) .padding(.vertical, 3) .background(Color.positiveGreen.opacity(0.12)) @@ -141,7 +141,7 @@ struct PaywallView: View { } label: { Image(systemName: "xmark") .font(.system(size: 14, weight: .semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .padding(10) .background(Color(.secondarySystemBackground)) .clipShape(Circle()) @@ -174,7 +174,7 @@ struct PaywallView: View { Text("One payment. Every feature. Forever.") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } } @@ -189,7 +189,7 @@ struct PaywallView: View { } .padding(20) .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -199,11 +199,11 @@ struct PaywallView: View { HStack(spacing: 4) { Text(iapService.formattedPrice) .font(.system(size: 28, weight: .bold, design: .rounded)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) Text("· one-time · Family Sharing") .font(.footnote) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -242,24 +242,24 @@ struct PaywallView: View { HStack(spacing: 6) { Text(title) .font(.subheadline.weight(.semibold)) - .foregroundColor(.primary) + .foregroundStyle(.primary) if let badge, !badge.isEmpty { Text(badge) .font(.caption2.weight(.bold)) .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color.appSecondary.opacity(0.15)) - .foregroundColor(.appSecondary) + .foregroundStyle(Color.appSecondary) .clipShape(Capsule()) } } Text(price) .font(.footnote) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Image(systemName: selectedPlan == plan ? "checkmark.circle.fill" : "circle") - .foregroundColor(selectedPlan == plan ? .appPrimary : .secondary) + .foregroundStyle(selectedPlan == plan ? Color.appPrimary : .secondary) } .padding(12) .background(Color(.systemBackground)) @@ -268,7 +268,7 @@ struct PaywallView: View { .stroke(selectedPlan == plan ? Color.appPrimary : Color.gray.opacity(0.25), lineWidth: selectedPlan == plan ? 2 : 1) ) - .cornerRadius(AppConstants.UI.smallCornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)) } .buttonStyle(.plain) } @@ -291,8 +291,8 @@ struct PaywallView: View { .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(AppConstants.UI.cornerRadius) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } .disabled(isPurchasing) } @@ -305,7 +305,7 @@ struct PaywallView: View { } label: { Text("Restore Purchases") .font(.subheadline) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } .disabled(isPurchasing) } @@ -316,17 +316,17 @@ struct PaywallView: View { VStack(spacing: 4) { Text("Payment charged to your Apple ID account.") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) HStack(spacing: 12) { Link("Terms", destination: URL(string: AppConstants.URLs.termsOfService)!) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Link("Privacy", destination: URL(string: AppConstants.URLs.privacyPolicy)!) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -381,7 +381,7 @@ struct BenefitRow: View { HStack(spacing: 14) { Image(systemName: icon) .font(.system(size: 20)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) .frame(width: 28) VStack(alignment: .leading, spacing: 1) { @@ -390,13 +390,13 @@ struct BenefitRow: View { Text(subtitle) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Image(systemName: "checkmark.circle.fill") - .foregroundColor(.positiveGreen) + .foregroundStyle(Color.positiveGreen) .font(.system(size: 18)) } .padding(.vertical, 2) @@ -439,7 +439,7 @@ struct CompactPaywallBanner: View { Text("Unlimited sources, advanced charts & more") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() @@ -452,13 +452,13 @@ struct CompactPaywallBanner: View { .padding(.horizontal, 12) .padding(.vertical, 6) .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(16) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 16)) } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } } @@ -493,8 +493,8 @@ struct PremiumLockOverlay: View { .padding(.horizontal, 20) .padding(.vertical, 10) .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(20) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 20)) } } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/PortfolioJournal/Views/Settings/AllocationTargetsView.swift b/PortfolioJournal/Views/Settings/AllocationTargetsView.swift index b6ee895..2b78a3f 100644 --- a/PortfolioJournal/Views/Settings/AllocationTargetsView.swift +++ b/PortfolioJournal/Views/Settings/AllocationTargetsView.swift @@ -11,13 +11,13 @@ struct AllocationTargetsView: View { Text("Total Targets") Spacer() Text(String(format: "%.0f%%", totalTargets)) - .foregroundColor(totalTargets == 100 ? .positiveGreen : .secondary) + .foregroundStyle(totalTargets == 100 ? Color.positiveGreen : .secondary) } if totalTargets != 100 { Text("Targets don't need to be perfect, but aiming for 100% keeps the drift view accurate.") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -40,7 +40,7 @@ struct AllocationTargetsView: View { .frame(width: 60) Text("%") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } header: { diff --git a/PortfolioJournal/Views/Settings/CSVMappingView.swift b/PortfolioJournal/Views/Settings/CSVMappingView.swift index c801569..300b66d 100644 --- a/PortfolioJournal/Views/Settings/CSVMappingView.swift +++ b/PortfolioJournal/Views/Settings/CSVMappingView.swift @@ -29,7 +29,7 @@ struct CSVMappingView: View { VStack(alignment: .leading, spacing: 2) { Text(pair.0) .font(.caption2.weight(.semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(pair.1.isEmpty ? "—" : pair.1) .font(.caption) .lineLimit(2) @@ -117,10 +117,10 @@ struct CSVMappingView: View { .navigationTitle("Map Columns") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Import") { onComplete(config) dismiss() @@ -261,10 +261,10 @@ private struct MappingRow: View { HStack(spacing: 4) { Text(selectionLabel) .font(.subheadline) - .foregroundColor(index == ImportService.CSVMappingConfig.notMapped ? .secondary : .appPrimary) + .foregroundStyle(index == ImportService.CSVMappingConfig.notMapped ? .secondary : Color.appPrimary) Image(systemName: "chevron.up.chevron.down") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -332,10 +332,10 @@ private struct MappingRowWithConstant: View { HStack(spacing: 4) { Text(selectionLabel) .font(.subheadline) - .foregroundColor(index == ImportService.CSVMappingConfig.notMapped ? .secondary : .appPrimary) + .foregroundStyle(index == ImportService.CSVMappingConfig.notMapped ? .secondary : Color.appPrimary) Image(systemName: "chevron.up.chevron.down") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } diff --git a/PortfolioJournal/Views/Settings/CategoriesView.swift b/PortfolioJournal/Views/Settings/CategoriesView.swift index 7078665..28665b7 100644 --- a/PortfolioJournal/Views/Settings/CategoriesView.swift +++ b/PortfolioJournal/Views/Settings/CategoriesView.swift @@ -13,7 +13,7 @@ struct CategoriesView: View { List { if categoryRepository.categories.isEmpty { Text(String(localized: "categories_empty")) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .center) .padding(.vertical, 32) } else { @@ -54,14 +54,14 @@ struct CategoriesView: View { .navigationTitle("Categories") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button { showingAddCategory = true } label: { Image(systemName: "plus") } } - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { EditButton() } } @@ -99,7 +99,7 @@ private struct CategoryRow: View { .frame(width: 40, height: 40) Image(systemName: category.icon) .font(.system(size: 16)) - .foregroundColor(category.color) + .foregroundStyle(category.color) } VStack(alignment: .leading, spacing: 2) { @@ -107,7 +107,7 @@ private struct CategoryRow: View { .font(.body) Text(category.sourceCount == 1 ? "1 source" : "\(category.sourceCount) sources") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() @@ -186,20 +186,20 @@ struct CategoryEditorView: View { .frame(width: 44, height: 44) Image(systemName: selectedIcon) .font(.system(size: 18)) - .foregroundColor(Color(hex: selectedColor) ?? .blue) + .foregroundStyle(Color(hex: selectedColor) ?? .blue) } Text(name.isEmpty ? String(localized: "category_name_placeholder") : name) - .foregroundColor(name.isEmpty ? .secondary : .primary) + .foregroundStyle(name.isEmpty ? .secondary : .primary) } } } .navigationTitle(category == nil ? "Add Category" : "Edit Category") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Save") { save() } .fontWeight(.semibold) .disabled(name.trimmingCharacters(in: .whitespaces).isEmpty) @@ -259,7 +259,7 @@ private struct IconSwatch: View { .frame(width: 44, height: 44) Image(systemName: icon) .font(.system(size: 18)) - .foregroundColor(isSelected ? color : .secondary) + .foregroundStyle(isSelected ? color : .secondary) } } } diff --git a/PortfolioJournal/Views/Settings/ImportDataView.swift b/PortfolioJournal/Views/Settings/ImportDataView.swift index 3b7bee3..8b23bf0 100644 --- a/PortfolioJournal/Views/Settings/ImportDataView.swift +++ b/PortfolioJournal/Views/Settings/ImportDataView.swift @@ -1,4 +1,5 @@ import SwiftUI +import CoreData import UniformTypeIdentifiers import UIKit @@ -88,7 +89,7 @@ struct ImportDataView: View { ProgressView() Text("Reviewing import…") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding(.top, 8) } @@ -98,7 +99,7 @@ struct ImportDataView: View { ProgressView(value: importProgress) Text(importStatus) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding(.top, 8) } @@ -133,7 +134,7 @@ struct ImportDataView: View { .navigationTitle("Import Data") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Done") { dismiss() } .disabled(isImporting) } @@ -244,7 +245,7 @@ Personal,Stocks,Index Fund,2024-01-01,15000,12000,Long-term .font(.caption.monospaced()) Text("Account, Contribution, and Notes are optional. Dates accept / or - and 24h/12h time.") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -325,7 +326,7 @@ Personal,Stocks,Index Fund,2024-01-01,15000,12000,Long-term Text("Account") Spacer() Text(selectedAccountName ?? "Personal") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } header: { @@ -333,7 +334,7 @@ Personal,Stocks,Index Fund,2024-01-01,15000,12000,Long-term } footer: { if let accountErrorMessage { Text(accountErrorMessage) - .foregroundColor(.negativeRed) + .foregroundStyle(Color.negativeRed) } } } diff --git a/PortfolioJournal/Views/Sources/AddSourceView.swift b/PortfolioJournal/Views/Sources/AddSourceView.swift index 9183b1d..8309329 100644 --- a/PortfolioJournal/Views/Sources/AddSourceView.swift +++ b/PortfolioJournal/Views/Sources/AddSourceView.swift @@ -1,4 +1,5 @@ import SwiftUI +import CoreData import StoreKit struct AddSourceView: View { @@ -54,25 +55,25 @@ struct AddSourceView: View { } label: { HStack { Text("Category") - .foregroundColor(.primary) + .foregroundStyle(.primary) Spacer() if let category = selectedCategory { HStack(spacing: 6) { Image(systemName: category.icon) - .foregroundColor(category.color) + .foregroundStyle(category.color) Text(category.name) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } else { Text("Select") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Image(systemName: "chevron.right") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } header: { @@ -80,10 +81,10 @@ struct AddSourceView: View { } footer: { if let error = duplicateError { Text(error) - .foregroundColor(.negativeRed) + .foregroundStyle(Color.negativeRed) } else { Text(String(localized: "add_source_name_footer")) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -92,7 +93,7 @@ struct AddSourceView: View { Section { HStack { Text(currencySymbol) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) TextField("0.00", text: $initialValue) .keyboardType(.decimalPad) @@ -106,13 +107,13 @@ struct AddSourceView: View { .navigationTitle("Add Source") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Add") { saveSource() } @@ -262,17 +263,17 @@ struct CategoryPickerView: View { .frame(width: 40, height: 40) Image(systemName: category.icon) - .foregroundColor(category.color) + .foregroundStyle(category.color) } Text(category.name) - .foregroundColor(.primary) + .foregroundStyle(.primary) Spacer() if selectedCategory?.id == category.id { Image(systemName: "checkmark") - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } } } @@ -280,7 +281,7 @@ struct CategoryPickerView: View { .navigationTitle("Select Category") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Done") { dismiss() } @@ -334,22 +335,22 @@ struct EditSourceView: View { } label: { HStack { Text("Category") - .foregroundColor(.primary) + .foregroundStyle(.primary) Spacer() if let category = selectedCategory { HStack(spacing: 6) { Image(systemName: category.icon) - .foregroundColor(category.color) + .foregroundStyle(category.color) Text(category.name) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } Image(systemName: "chevron.right") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -357,13 +358,13 @@ struct EditSourceView: View { .navigationTitle("Edit Source") .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button("Save") { saveChanges() } @@ -456,7 +457,7 @@ struct AddSnapshotView: View { HStack { Text(viewModel.currencySymbol) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) TextField("Value", text: $viewModel.valueString) .keyboardType(.decimalPad) @@ -474,7 +475,7 @@ struct AddSnapshotView: View { if viewModel.previousValue != nil { Text(viewModel.previousValueString) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } header: { Text("Snapshot Details") @@ -488,10 +489,10 @@ struct AddSnapshotView: View { Text("Change from previous") Spacer() Text("\(change) (\(percentage))") - .foregroundColor( + .foregroundStyle( (viewModel.changeFromPrevious ?? 0) >= 0 - ? .positiveGreen - : .negativeRed + ? Color.positiveGreen + : Color.negativeRed ) } } @@ -503,7 +504,7 @@ struct AddSnapshotView: View { if viewModel.includeContribution { HStack { Text(viewModel.currencySymbol) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) TextField("New capital added", text: $viewModel.contributionString) .keyboardType(.decimalPad) @@ -525,13 +526,13 @@ struct AddSnapshotView: View { .navigationTitle(viewModel.title) .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarLeading) { + ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } } - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Button(viewModel.buttonTitle) { saveSnapshot() } diff --git a/PortfolioJournal/Views/Sources/SourceDetailView.swift b/PortfolioJournal/Views/Sources/SourceDetailView.swift index 8b613b0..7eee2d2 100644 --- a/PortfolioJournal/Views/Sources/SourceDetailView.swift +++ b/PortfolioJournal/Views/Sources/SourceDetailView.swift @@ -66,7 +66,7 @@ struct SourceDetailView: View { .navigationTitle(viewModel.safeSourceName) .navigationBarTitleDisplayMode(.inline) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItem(placement: .topBarTrailing) { Menu { Button { viewModel.showingEditSource = true @@ -129,11 +129,11 @@ struct SourceDetailView: View { Text(viewModel.categoryName) } .font(.caption) - .foregroundColor(Color(hex: viewModel.categoryColor) ?? .gray) + .foregroundStyle(Color(hex: viewModel.categoryColor) ?? .gray) .padding(.horizontal, 12) .padding(.vertical, 6) .background((Color(hex: viewModel.categoryColor) ?? .gray).opacity(0.1)) - .cornerRadius(20) + .clipShape(RoundedRectangle(cornerRadius: 20)) // Current value Text(viewModel.formattedCurrentValue) @@ -147,17 +147,17 @@ struct SourceDetailView: View { Text("(\(viewModel.formattedPercentageReturn))") } .font(.subheadline.weight(.medium)) - .foregroundColor(viewModel.isPositiveReturn ? .positiveGreen : .negativeRed) + .foregroundStyle(viewModel.isPositiveReturn ? Color.positiveGreen : Color.negativeRed) // Last updated Text("Last updated: \(viewModel.lastUpdated)") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity) .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -172,8 +172,8 @@ struct SourceDetailView: View { .frame(maxWidth: .infinity) .padding() .background(Color.appPrimary) - .foregroundColor(.white) - .cornerRadius(AppConstants.UI.cornerRadius) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) } } @@ -186,7 +186,7 @@ struct SourceDetailView: View { HStack { Label(String(localized: "source_monthly_contribution_title"), systemImage: "arrow.down.circle.fill") .font(.subheadline.weight(.semibold)) - .foregroundColor(.primary) + .foregroundStyle(.primary) Spacer() Button { if editingContribution { @@ -204,7 +204,7 @@ struct SourceDetailView: View { } label: { Text(editingContribution ? String(localized: "done") : String(localized: "edit")) .font(.caption.weight(.semibold)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) } } @@ -213,20 +213,20 @@ struct SourceDetailView: View { .keyboardType(.decimalPad) .padding(8) .background(Color(.systemGray6)) - .cornerRadius(8) + .clipShape(RoundedRectangle(cornerRadius: 8)) } else { Text(current.map { $0.currencyString } ?? String(localized: "source_monthly_contribution_not_set")) .font(.subheadline) - .foregroundColor(current != nil ? .primary : .secondary) + .foregroundStyle(current != nil ? .primary : .secondary) } Text(String(localized: "source_monthly_contribution_hint")) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) .confirmationDialog( String(localized: "source_monthly_contribution_apply_title"), @@ -316,7 +316,7 @@ struct SourceDetailView: View { .padding(.horizontal, 6) .padding(.vertical, 4) .background(Color.appSecondary.opacity(0.15)) - .cornerRadius(8) + .clipShape(RoundedRectangle(cornerRadius: 8)) } } } @@ -347,18 +347,18 @@ struct SourceDetailView: View { Text("Unlock predictions on the chart") } .font(.caption.weight(.semibold)) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) .padding(.vertical, 6) .padding(.horizontal, 10) .background(Color.appPrimary.opacity(0.1)) - .cornerRadius(10) + .clipShape(RoundedRectangle(cornerRadius: 10)) } .frame(maxWidth: .infinity, alignment: .leading) } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -383,7 +383,7 @@ struct SourceDetailView: View { } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } @@ -399,13 +399,13 @@ struct SourceDetailView: View { Text("\(viewModel.snapshotCount)") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } if viewModel.isHistoryLimited { HStack { Image(systemName: "info.circle") - .foregroundColor(.appWarning) + .foregroundStyle(Color.appWarning) Text("\(viewModel.hiddenSnapshotCount) older snapshots hidden. Upgrade for full history.") .font(.caption) @@ -418,7 +418,7 @@ struct SourceDetailView: View { } .padding(8) .background(Color.appWarning.opacity(0.1)) - .cornerRadius(AppConstants.UI.smallCornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.smallCornerRadius)) } // Use LazyVStack for better performance with many snapshots @@ -464,12 +464,12 @@ struct SourceDetailView: View { if viewModel.isHistoryLimited && viewModel.hiddenSnapshotCount > 0 { Text("+ \(viewModel.hiddenSnapshotCount) older snapshots hidden") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } .padding() .background(Color(.systemBackground)) - .cornerRadius(AppConstants.UI.cornerRadius) + .clipShape(RoundedRectangle(cornerRadius: AppConstants.UI.cornerRadius)) .shadow(color: .black.opacity(0.05), radius: 8, y: 2) } } @@ -484,7 +484,7 @@ struct MetricCard: View { VStack(alignment: .leading, spacing: 4) { Text(title) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(value) .font(.subheadline.weight(.semibold)) @@ -508,7 +508,7 @@ struct SnapshotRowView: View { if let notes = snapshot.notes, !notes.isEmpty { Text(notes) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .lineLimit(1) } } @@ -523,7 +523,7 @@ struct SnapshotRowView: View { if snapshot.contribution != nil && snapshot.decimalContribution > 0 { Text("+ \(snapshot.decimalContribution.currencyString)") .font(.caption) - .foregroundColor(.appPrimary) + .foregroundStyle(Color.appPrimary) .hiddenBalance() } } @@ -532,10 +532,10 @@ struct SnapshotRowView: View { Button(action: onEdit) { Image(systemName: "pencil") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .padding(6) .background(Color.gray.opacity(0.1)) - .cornerRadius(8) + .clipShape(RoundedRectangle(cornerRadius: 8)) } .buttonStyle(.plain) } @@ -552,7 +552,7 @@ struct MetricChip: View { VStack(alignment: .leading, spacing: 4) { Text(title) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(value) .font(.caption.weight(.semibold)) @@ -561,7 +561,7 @@ struct MetricChip: View { .padding(.vertical, 10) .padding(.horizontal, 12) .background(Color.gray.opacity(0.1)) - .cornerRadius(10) + .clipShape(RoundedRectangle(cornerRadius: 10)) } } diff --git a/PortfolioJournalQuickUpdate/ShareViewController.swift b/PortfolioJournalQuickUpdate/ShareViewController.swift index e320a80..2f3fd40 100644 --- a/PortfolioJournalQuickUpdate/ShareViewController.swift +++ b/PortfolioJournalQuickUpdate/ShareViewController.swift @@ -147,7 +147,7 @@ struct QuickUpdateShareView: View { ProgressView() Text(String(localized: "ext_scanning_image")) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } else { TextField(String(localized: "ext_amount_placeholder"), text: $amountText) @@ -157,7 +157,7 @@ struct QuickUpdateShareView: View { if scanFoundNothing { Text(String(localized: "ext_ocr_no_amounts")) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -187,7 +187,7 @@ struct QuickUpdateShareView: View { lineWidth: 1 ) ) - .foregroundColor(.primary) + .foregroundStyle(.primary) } .buttonStyle(.plain) } @@ -205,18 +205,18 @@ struct QuickUpdateShareView: View { HStack { VStack(alignment: .leading, spacing: 2) { Text(source.name) - .foregroundColor(.primary) + .foregroundStyle(.primary) .font(.subheadline.weight(.medium)) if source.updatedThisMonth { Text(String(localized: "ext_already_updated")) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } Spacer() if selectedSourceId == source.id { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.blue) + .foregroundStyle(.blue) } } } @@ -229,12 +229,12 @@ struct QuickUpdateShareView: View { VStack(spacing: 14) { Image(systemName: "checkmark.circle.fill") .font(.system(size: 52)) - .foregroundColor(.green) + .foregroundStyle(.green) Text(String(localized: "ext_saved_title")) .font(.headline) Text(String(localized: "ext_saved_body")) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } .padding(32) diff --git a/PortfolioJournalWidget/InvestmentWidget.swift b/PortfolioJournalWidget/InvestmentWidget.swift index 047f96d..80bf110 100644 --- a/PortfolioJournalWidget/InvestmentWidget.swift +++ b/PortfolioJournalWidget/InvestmentWidget.swift @@ -579,9 +579,9 @@ struct ChangeLine: View { .font(.caption.weight(.medium)) Text("(\(String(format: "%.1f%%", entry.dayChangePercentage)))") .font(.caption2) - .foregroundColor(compact ? (entry.dayChange >= 0 ? .green : .red) : .secondary) + .foregroundStyle(compact ? (entry.dayChange >= 0 ? .green : .red) : .secondary) } - .foregroundColor(entry.dayChange >= 0 ? .green : .red) + .foregroundStyle(entry.dayChange >= 0 ? .green : .red) .lineLimit(1) .minimumScaleFactor(0.8) } @@ -595,7 +595,7 @@ struct StreakPill: View { HStack(spacing: 3) { Image(systemName: "flame.fill") .font(.caption2) - .foregroundColor(.orange) + .foregroundStyle(.orange) Text("\(streak) mo") .font(.caption2.weight(.semibold)) } @@ -608,10 +608,10 @@ struct CheckInPill: View { HStack(spacing: 3) { Image(systemName: "calendar.badge.clock") .font(.caption2) - .foregroundColor(checkInIsUrgent(date) ? .orange : .secondary) + .foregroundStyle(checkInIsUrgent(date) ? .orange : .secondary) Text(checkInText(for: date)) .font(.caption2.weight(.medium)) - .foregroundColor(checkInIsUrgent(date) ? .orange : .secondary) + .foregroundStyle(checkInIsUrgent(date) ? .orange : .secondary) } .lineLimit(1) } @@ -629,25 +629,25 @@ struct GoalProgressRow: View { HStack { Image(systemName: "target") .font(.caption2) - .foregroundColor(widgetSecondaryColor) + .foregroundStyle(widgetSecondaryColor) Text(goal.name) .font(.caption.weight(.semibold)) .lineLimit(1) Spacer() Text("\(Int((pct * 100).rounded()))%") .font(.caption.weight(.bold)) - .foregroundColor(widgetSecondaryColor) + .foregroundStyle(widgetSecondaryColor) } ProgressView(value: pct) .tint(widgetSecondaryColor) HStack { Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() Text(goal.targetAmount.compactCurrencyString(currencyCode: entry.currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } else if entry.nextMilestone > 0 { @@ -655,13 +655,13 @@ struct GoalProgressRow: View { HStack { Image(systemName: "flag.checkered") .font(.caption2) - .foregroundColor(widgetSecondaryColor) + .foregroundStyle(widgetSecondaryColor) Text("Next milestone") .font(.caption.weight(.semibold)) Spacer() Text("\(Int((entry.milestoneProgress * 100).rounded()))%") .font(.caption.weight(.bold)) - .foregroundColor(widgetSecondaryColor) + .foregroundStyle(widgetSecondaryColor) } ProgressView(value: entry.milestoneProgress) .tint(widgetSecondaryColor) @@ -669,7 +669,7 @@ struct GoalProgressRow: View { Spacer() Text(entry.nextMilestone.compactCurrencyString(currencyCode: entry.currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -687,7 +687,7 @@ struct SmallWidgetView: View { VStack(alignment: .leading, spacing: 6) { Text("Total Value") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode)) .font(.title2.weight(.bold)) @@ -697,7 +697,7 @@ struct SmallWidgetView: View { ChangeLine(entry: entry, compact: true) Text("since last check-in") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer(minLength: 0) @@ -723,12 +723,12 @@ struct EmptyStateView: View { VStack(spacing: 8) { Image(systemName: "chart.line.uptrend.xyaxis") .font(.title) - .foregroundColor(widgetPrimaryColor) + .foregroundStyle(widgetPrimaryColor) Text("No data yet") .font(.subheadline.weight(.semibold)) Text("Add a snapshot to start tracking.") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity, maxHeight: .infinity) @@ -757,7 +757,7 @@ struct MediumWidgetView: View { HStack(spacing: 6) { Text("Portfolio") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer(minLength: 0) WidgetRefreshButton() } @@ -791,20 +791,20 @@ struct MediumWidgetView: View { VStack(alignment: .trailing, spacing: 4) { Text("Add snapshots") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("to see trend") .font(.caption.weight(.semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } else { VStack(alignment: .trailing, spacing: 4) { Text("Sparkline") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("Premium") .font(.caption.weight(.semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -818,7 +818,7 @@ struct MediumWidgetView: View { Text(source.name) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .lineLimit(1) Text(source.value.shortCurrencyString(currencyCode: entry.currencyCode)) @@ -831,14 +831,14 @@ struct MediumWidgetView: View { HStack(spacing: 4) { Image(systemName: "lightbulb.fill") .font(.caption2) - .foregroundColor(.orange) + .foregroundStyle(.orange) VStack(alignment: .leading, spacing: 0) { Text(entry.insightTitle) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(entry.insightValue) .font(.caption.weight(.semibold)) - .foregroundColor(.primary) + .foregroundStyle(.primary) .lineLimit(1) } } @@ -874,7 +874,7 @@ struct LargeWidgetView: View { VStack(alignment: .leading, spacing: 6) { Text("Portfolio") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode)) .font(.title2.weight(.bold)) @@ -914,7 +914,7 @@ struct LargeWidgetView: View { Text(category.name) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() @@ -929,7 +929,7 @@ struct LargeWidgetView: View { .font(.caption.weight(.semibold)) Text("Category evolution appears after updates.") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) } @@ -939,7 +939,7 @@ struct LargeWidgetView: View { .font(.caption.weight(.semibold)) Text("Premium shows evolution by category.") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) } @@ -1007,7 +1007,7 @@ struct AccessoryRectangularView: View { VStack(alignment: .leading, spacing: 2) { Text("Portfolio") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode)) .font(.headline) @@ -1019,7 +1019,7 @@ struct AccessoryRectangularView: View { Text(String(format: "%.1f%%", entry.dayChangePercentage)) .font(.caption2) } - .foregroundColor(entry.dayChange >= 0 ? .green : .red) + .foregroundStyle(entry.dayChange >= 0 ? .green : .red) } .containerBackground(.background, for: .widget) } @@ -1057,11 +1057,11 @@ struct TrendLineChartView: View { VStack(alignment: .leading, spacing: 2) { Text(Decimal(maxValue).shortCurrencyString(currencyCode: currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() Text(Decimal(minValue).shortCurrencyString(currencyCode: currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } VStack(spacing: 4) { @@ -1109,7 +1109,7 @@ struct TrendLineChartView: View { ForEach(labels.indices, id: \.self) { index in Text(labels[index]) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .frame(maxWidth: .infinity) } } @@ -1154,11 +1154,11 @@ struct CombinedCategoryChartView: View { VStack(alignment: .leading, spacing: 2) { Text(Decimal(maxValue).shortCurrencyString(currencyCode: currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Spacer() Text(Decimal(0).shortCurrencyString(currencyCode: currencyCode)) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } VStack(spacing: 4) { @@ -1220,7 +1220,7 @@ struct CombinedCategoryChartView: View { ForEach(labels.indices, id: \.self) { index in Text(labels[index]) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .frame(maxWidth: .infinity) } } @@ -1241,7 +1241,7 @@ struct WidgetRefreshButton: View { Button(intent: RefreshWidgetIntent()) { Image(systemName: "arrow.clockwise") .font(.caption2.weight(.semibold)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .buttonStyle(.plain) .invalidatableContent() diff --git a/Shared/WatchPortfolioSnapshot.swift b/Shared/WatchPortfolioSnapshot.swift index 7746701..1df44f2 100644 --- a/Shared/WatchPortfolioSnapshot.swift +++ b/Shared/WatchPortfolioSnapshot.swift @@ -86,7 +86,7 @@ struct WatchPortfolioSnapshot: Codable, Equatable { /// Message keys exchanged over WatchConnectivity. enum WatchSyncMessage { /// The watch asking the phone for a freshly built snapshot. - static let requestSnapshot = "requestSnapshot" + nonisolated static let requestSnapshot = "requestSnapshot" } // MARK: - Transport