2.0: mostrar los dos ultimos pasos del onboarding (aha moment + paywall)
completeOnboarding escribia settings.onboardingCompleted al terminar el paso de platos. Ese flag es el que ContentView observa para cambiar de OnboardingView a HomeView, asi que el save destruia el flujo antes de que nextStep() pudiera pintar el paso 5. WeekReadyStepView (el aha moment de la 1.2.0) y PaywallStepView no se han visto nunca. GA4 lo confirma: 3 onboarding_completed en 28 dias y cero eventos con step 5 o 6. Se separa persistir los datos de marcar el onboarding como terminado: completeOnboarding(markFinished:) commitea platos y plan sin tocar el flag, y el nuevo finishOnboarding() lo escribe al salir del paywall. HomeView ya difiere su prompt premium a la segunda sesion, asi que no hay paywall duplicado. De paso, los nombres de paso en AnalyticsService estaban desalineados: el indice 5 decia "5_paywall" cuando el paso 5 es WeekReady, y el 6 no tenia nombre. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
This commit is contained in:
@@ -120,8 +120,15 @@ final class OnboardingViewModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Persists the choices made during onboarding (settings, dishes, first week).
|
||||
///
|
||||
/// `markFinished` must stay `false` while the flow is still on screen: the
|
||||
/// `onboardingCompleted` flag is what ContentView watches to swap onboarding
|
||||
/// for the home, so writing it here would tear the flow down before the last
|
||||
/// two steps (Week Ready and paywall) get a chance to render. Call
|
||||
/// `finishOnboarding(context:)` once the user leaves the final step.
|
||||
@discardableResult
|
||||
func completeOnboarding(context: ModelContext) -> Bool {
|
||||
func completeOnboarding(context: ModelContext, markFinished: Bool = true) -> Bool {
|
||||
// Create or get settings
|
||||
let descriptor = FetchDescriptor<AppSettings>()
|
||||
let settings = (try? context.fetch(descriptor))?.first ?? {
|
||||
@@ -137,7 +144,9 @@ final class OnboardingViewModel: ObservableObject {
|
||||
settings.calendarId = selectedCalendarId
|
||||
settings.lunchTime = lunchTime
|
||||
settings.dinnerTime = dinnerTime
|
||||
settings.onboardingCompleted = true
|
||||
if markFinished {
|
||||
settings.onboardingCompleted = true
|
||||
}
|
||||
|
||||
// Create default tags if not exist
|
||||
let tagDescriptor = FetchDescriptor<Tag>()
|
||||
@@ -178,6 +187,23 @@ final class OnboardingViewModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Flips the flag ContentView watches, handing the user over to the home.
|
||||
/// Separate from `completeOnboarding` so the data can be committed at the
|
||||
/// dishes step while the flow stays on screen for the last two steps.
|
||||
func finishOnboarding(context: ModelContext) {
|
||||
let descriptor = FetchDescriptor<AppSettings>()
|
||||
guard let settings = (try? context.fetch(descriptor))?.first else { return }
|
||||
guard !settings.onboardingCompleted else { return }
|
||||
|
||||
settings.onboardingCompleted = true
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
CrashlyticsService.record(error, context: "onboarding_finish")
|
||||
print("Onboarding finish failed: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills the first week right inside onboarding (the "aha moment" step) using
|
||||
/// the same engine as the home auto-complete. Returns the number of filled slots.
|
||||
@discardableResult
|
||||
|
||||
Reference in New Issue
Block a user