Onboarding: prompts fire on forward-leave of their page, not arrival

b634c06 fired on page arrival, so swiping stacked Location + Notifications
before a screen was read. Now each permission fires only when the user
ADVANCES PAST its page (swipe forward or Continue) — the screen has been
read by then. Forward-only (swiping back never fires), idempotent
(already-determined → status refresh), so replay and auto-advance stay
clean. Continue buttons are now pure navigation; denied paths unchanged.
This commit is contained in:
FuelBoard Contributor
2026-08-15 14:44:18 +01:00
parent b634c0686d
commit 44a8e7f42a
+14 -19
View File
@@ -54,15 +54,17 @@ struct OnboardingView: View {
.onChange(of: prompter.notificationsGranted) { _, granted in
if granted, page == 2 { page = 3 }
}
// Fire the permission the moment its page comes into view swiping
// must trigger the prompt exactly like tapping Continue does. Both
// request methods are idempotent: an already-determined permission
// just refreshes the status (no duplicate system alert), so this is
// safe for replay and for the auto-advance path.
.onChange(of: page) { _, newPage in
if newPage == 1 {
// Fire each permission when the user LEAVES its page by advancing
// forward (swipe or Continue) by then the screen has been read.
// Never on arrival: a fast swipe can't stack prompts before a page
// is seen. Forward-only: swiping back never fires. Idempotent
// an already-determined permission just refreshes, so the
// auto-advance path and replays stay clean (no duplicate alerts).
.onChange(of: page) { oldPage, newPage in
guard newPage > oldPage else { return }
if oldPage == 1 {
prompter.requestLocation()
} else if newPage == 2 {
} else if oldPage == 2 {
prompter.requestNotifications()
}
}
@@ -246,12 +248,10 @@ struct OnboardingView: View {
) {
if prompter.locationDenied {
openSettings()
} else if !prompter.locationGranted {
// Safety net the prompt already fired when this page
// came into view (see onChange(of: page)); re-requesting
// is a no-op unless the status is still undetermined.
prompter.requestLocation()
} else {
// The prompt fires on LEAVING this page (onChange(of:
// page), forward advance only) the description is on
// screen until the user moves on.
page = 2
}
}
@@ -259,13 +259,8 @@ struct OnboardingView: View {
primaryButton(
prompter.notificationsDenied ? "Continue without alerts" : (prompter.notificationsGranted ? "Continue" : "Allow Notifications")
) {
if prompter.notificationsDenied {
// Prompt fires on leaving this page (same rule as Location).
page = 3
} else if !prompter.notificationsGranted {
prompter.requestNotifications()
} else {
page = 3
}
}
case 3:
// No permission on this page prices need none. Straight on.