1.0.5: Eating out slots, rule violations panel, Crashlytics, search bar fix
- Eating out: tap any empty slot → "Mark as eating out"; shows teal indicator, skipped by auto-assign, counts as complete, tap again to unmark - Rule violations panel: access via wand long-press context menu when conflicts exist; shows all isRuleOverridden slots with Fix (clear) or Ignore (acknowledge) actions - Firebase Crashlytics integrated: CrashlyticsService + dSYM upload build phase, isPremium property tracked per session - PremiumSyncService: extracted premium state machine, StoreKit Transaction.updates listener, isPremium no longer synced via iCloud to avoid stale state - StoreManager: analytics on purchase/restore, bundle ID fallback for product ID lookup - Search bar contrast bug fixed: TextField now has explicit foreground color for dark mode - WelcomeStepView: redesigned onboarding welcome screen with week preview - Version bump: 1.0.5 build 22 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,24 +4,26 @@ struct WelcomeStepView: View {
|
||||
var onNext: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 32) {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
AppIconPlaceholder(size: 120)
|
||||
AppIconPlaceholder(size: 96)
|
||||
.padding(.bottom, 12)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
Text("onboarding_welcome_subtitle")
|
||||
.font(.mealMoodBody)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
Text("onboarding_welcome_subtitle")
|
||||
.font(.mealMoodBody)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 32)
|
||||
.padding(.bottom, 24)
|
||||
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
BenefitRow(icon: "1.circle.fill", text: String(localized: "onboarding_welcome_step_1"))
|
||||
BenefitRow(icon: "2.circle.fill", text: String(localized: "onboarding_welcome_step_2"))
|
||||
BenefitRow(icon: "3.circle.fill", text: String(localized: "onboarding_welcome_step_3"))
|
||||
BenefitRow(icon: "4.circle.fill", text: String(localized: "onboarding_welcome_step_4"))
|
||||
weekPreview
|
||||
.padding(.bottom, 24)
|
||||
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
BenefitRow(icon: "wand.and.stars", text: String(localized: "onboarding_welcome_step_1"))
|
||||
BenefitRow(icon: "square.and.arrow.up", text: String(localized: "onboarding_welcome_step_3"))
|
||||
BenefitRow(icon: "icloud", text: String(localized: "onboarding_welcome_step_4"))
|
||||
}
|
||||
.padding(.horizontal, 40)
|
||||
|
||||
@@ -33,6 +35,115 @@ struct WelcomeStepView: View {
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
}
|
||||
|
||||
// MARK: - Week preview
|
||||
|
||||
private var weekPreview: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(String(localized: "onboarding_welcome_preview_label"))
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
.padding(.horizontal, 24)
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 6) {
|
||||
ForEach(sampleDays, id: \.name) { day in
|
||||
VStack(spacing: 4) {
|
||||
Text(day.name)
|
||||
.font(.system(size: 10, weight: .bold))
|
||||
.foregroundColor(.mealMoodTextPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 5)
|
||||
.background(day.color)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
Text(day.dish)
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.mealMoodTextPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
.lineLimit(2)
|
||||
.minimumScaleFactor(0.8)
|
||||
.frame(maxWidth: .infinity, minHeight: 38)
|
||||
.padding(.horizontal, 4)
|
||||
.padding(.vertical, 5)
|
||||
.background(Color.white.opacity(0.92))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.stroke(Color(hex: "#EEE4DE"), lineWidth: 1)
|
||||
)
|
||||
}
|
||||
.frame(width: 66)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 14)
|
||||
.fill(Color.mealMoodBackground)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 14)
|
||||
.stroke(Color.mealMoodCoral.opacity(0.25), lineWidth: 1)
|
||||
)
|
||||
)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
|
||||
private var sampleDays: [(name: String, dish: String, color: Color)] {
|
||||
let lang = Locale.current.language.languageCode?.identifier ?? "en"
|
||||
switch lang {
|
||||
case "es":
|
||||
return [
|
||||
("Lun", "Pasta al horno", Color(hex: "#FFD5D9")),
|
||||
("Mar", "Pollo asado", Color(hex: "#FFE1C8")),
|
||||
("Mié", "Lentejas", Color(hex: "#FFF0C8")),
|
||||
("Jue", "Salmón", Color(hex: "#DFF2CC")),
|
||||
("Vie", "Tortilla", Color(hex: "#D3F0E7"))
|
||||
]
|
||||
case "fr":
|
||||
return [
|
||||
("Lun", "Pâtes gratinées", Color(hex: "#FFD5D9")),
|
||||
("Mar", "Poulet rôti", Color(hex: "#FFE1C8")),
|
||||
("Mer", "Lentilles", Color(hex: "#FFF0C8")),
|
||||
("Jeu", "Saumon", Color(hex: "#DFF2CC")),
|
||||
("Ven", "Omelette", Color(hex: "#D3F0E7"))
|
||||
]
|
||||
case "de":
|
||||
return [
|
||||
("Mo", "Nudeln", Color(hex: "#FFD5D9")),
|
||||
("Di", "Hähnchen", Color(hex: "#FFE1C8")),
|
||||
("Mi", "Linsensuppe", Color(hex: "#FFF0C8")),
|
||||
("Do", "Lachs", Color(hex: "#DFF2CC")),
|
||||
("Fr", "Omelett", Color(hex: "#D3F0E7"))
|
||||
]
|
||||
case "it":
|
||||
return [
|
||||
("Lun", "Pasta al forno", Color(hex: "#FFD5D9")),
|
||||
("Mar", "Pollo arrosto", Color(hex: "#FFE1C8")),
|
||||
("Mer", "Lenticchie", Color(hex: "#FFF0C8")),
|
||||
("Gio", "Salmone", Color(hex: "#DFF2CC")),
|
||||
("Ven", "Frittata", Color(hex: "#D3F0E7"))
|
||||
]
|
||||
case "pt":
|
||||
return [
|
||||
("Seg", "Macarrão", Color(hex: "#FFD5D9")),
|
||||
("Ter", "Frango assado", Color(hex: "#FFE1C8")),
|
||||
("Qua", "Lentilhas", Color(hex: "#FFF0C8")),
|
||||
("Qui", "Salmão", Color(hex: "#DFF2CC")),
|
||||
("Sex", "Omelete", Color(hex: "#D3F0E7"))
|
||||
]
|
||||
default:
|
||||
return [
|
||||
("Mon", "Baked pasta", Color(hex: "#FFD5D9")),
|
||||
("Tue", "Roast chicken", Color(hex: "#FFE1C8")),
|
||||
("Wed", "Lentil soup", Color(hex: "#FFF0C8")),
|
||||
("Thu", "Salmon", Color(hex: "#DFF2CC")),
|
||||
("Fri", "Omelette", Color(hex: "#D3F0E7"))
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct BenefitRow: View {
|
||||
@@ -42,7 +153,7 @@ private struct BenefitRow: View {
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 20))
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(.mealMoodCoral)
|
||||
.frame(width: 28)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user