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:
@@ -54,15 +54,17 @@ struct OnboardingView: View {
|
|||||||
.onChange(of: prompter.notificationsGranted) { _, granted in
|
.onChange(of: prompter.notificationsGranted) { _, granted in
|
||||||
if granted, page == 2 { page = 3 }
|
if granted, page == 2 { page = 3 }
|
||||||
}
|
}
|
||||||
// Fire the permission the moment its page comes into view — swiping
|
// Fire each permission when the user LEAVES its page by advancing
|
||||||
// must trigger the prompt exactly like tapping Continue does. Both
|
// forward (swipe or Continue) — by then the screen has been read.
|
||||||
// request methods are idempotent: an already-determined permission
|
// Never on arrival: a fast swipe can't stack prompts before a page
|
||||||
// just refreshes the status (no duplicate system alert), so this is
|
// is seen. Forward-only: swiping back never fires. Idempotent —
|
||||||
// safe for replay and for the auto-advance path.
|
// an already-determined permission just refreshes, so the
|
||||||
.onChange(of: page) { _, newPage in
|
// auto-advance path and replays stay clean (no duplicate alerts).
|
||||||
if newPage == 1 {
|
.onChange(of: page) { oldPage, newPage in
|
||||||
|
guard newPage > oldPage else { return }
|
||||||
|
if oldPage == 1 {
|
||||||
prompter.requestLocation()
|
prompter.requestLocation()
|
||||||
} else if newPage == 2 {
|
} else if oldPage == 2 {
|
||||||
prompter.requestNotifications()
|
prompter.requestNotifications()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,12 +248,10 @@ struct OnboardingView: View {
|
|||||||
) {
|
) {
|
||||||
if prompter.locationDenied {
|
if prompter.locationDenied {
|
||||||
openSettings()
|
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 {
|
} 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
|
page = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,13 +259,8 @@ struct OnboardingView: View {
|
|||||||
primaryButton(
|
primaryButton(
|
||||||
prompter.notificationsDenied ? "Continue without alerts" : (prompter.notificationsGranted ? "Continue" : "Allow Notifications")
|
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
|
page = 3
|
||||||
} else if !prompter.notificationsGranted {
|
|
||||||
prompter.requestNotifications()
|
|
||||||
} else {
|
|
||||||
page = 3
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
// No permission on this page — prices need none. Straight on.
|
// No permission on this page — prices need none. Straight on.
|
||||||
|
|||||||
Reference in New Issue
Block a user