Xcode 27: compila sin warnings y migra API deprecadas de SwiftUI

- Imports de CoreData que faltaban (MemberImportVisibility) en AccountsView,
  ImportDataView, ChartsContainerView y AddSourceView.
- Aislamiento MainActor: IsolatedDefaultValues en el target de la app,
  LinearGradient @MainActor, WatchSyncMessage nonisolated, closures de
  ReviewPromptService/MonthlyCheckInStore/AppIntents.
- Warnings de valores sin usar (SnapshotGapDetector, DashboardLayoutStore,
  OnboardingView, GoalEditorView, CoreDataStack, SettingsViewModel).
- foregroundColor→foregroundStyle, cornerRadius→clipShape,
  navigationBarLeading/Trailing→topBarLeading/Trailing,
  Task.sleep(nanoseconds:)→sleep(for:), NavigationLink(isActive:)→
  navigationDestination(isPresented:).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
This commit is contained in:
alexandrev-tibco
2026-09-16 11:44:49 +02:00
parent 464fbb2bad
commit a8a9ad64f3
39 changed files with 420 additions and 414 deletions
@@ -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)
}
@@ -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
@@ -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)
}
}
@@ -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)
@@ -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)
}
}
}
@@ -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 {
@@ -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)
}
}
}
@@ -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))
@@ -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)
@@ -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)
@@ -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()
@@ -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)
}
}
}
@@ -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)
@@ -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)
}
@@ -26,7 +26,7 @@ struct GoalProgressBar: View {
.overlay(alignment: .leading) {
Image(systemName: iconName)
.font(.caption2)
.foregroundColor(iconColor)
.foregroundStyle(iconColor)
.offset(x: iconOffset)
}
}
@@ -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))
}
}
}
+22 -22
View File
@@ -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))
@@ -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)
@@ -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)
@@ -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)
@@ -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: {
@@ -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)
}
}
}
@@ -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)
}
}
}
@@ -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)
}
}
}
@@ -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()
}
@@ -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))
}
}