[auth-payment-login-lockout] Make auth intro a clean screen with a back button

App Review rejected the 2026-08 build: tapping checkout as a guest
switched root to .auth, which replaced the whole UI with LoginView as the
NavigationStack root - no nav bar, no back, no dismiss. User was trapped.

- LoginView: rebuilt as a static screen in LCENavigationView with a back
  button that sets root = .main, a 'Criar conta' and an 'Entrar' button,
  and 'Termos de Uso' / 'Politica de Privacidade' links. Added #Preview.
- Forced .preferredColorScheme(.light) and fixed DS colors (the app has
  no dark theme, so system dark mode was inverting the nav bar / title).
- Removed the entry-reveal animation machinery (heroVisible/textVisible/
  buttonVisible/token/prepare flags across LoginView, AuthFlowView,
  ContentView) - dead since guest browsing shipped and the root cause of
  the 2026-08-06 'content stuck hidden' bug.
- enterAuthFlow() is now just root = .auth.
- UITestSupport.ensureLoggedIn taps 'Entrar' (new label).
- ProfileLoggedOutFlowTests: new test covering the choices and the way
  back out of .auth.
- Localizable.xcstrings: catalog caught up to the new/removed strings.
This commit is contained in:
Daniel Arantes Loverde
2026-08-27 10:11:04 -03:00
parent 662f7d6ebd
commit 57103c637a
6 changed files with 81 additions and 170 deletions

View File

@@ -15,8 +15,6 @@ struct ContentView: View {
@State var hasPerformedInitialLaunchBootstrap = false
@State var showLaunchSplash = true
@State var shouldPulseLaunchSplash = true
@State var shouldPrepareAuthEntryAnimation = DefaultTokenStore().jwt == nil
@State var authEntryAnimationToken = 0
@State private var sessionExpiredObserver: NSObjectProtocol?
@State var cartResetObserver: Any?
@State var appResumeObserver: Any?
@@ -32,9 +30,7 @@ struct ContentView: View {
root: $root,
selectedTab: $selectedTab,
tokenStore: tokenStore,
appState: $appState,
shouldPrepareLoginEntry: shouldPrepareAuthEntryAnimation,
authEntryAnimationToken: authEntryAnimationToken
appState: $appState
)
case .main:
if isBootstrappingSession {
@@ -136,15 +132,11 @@ struct ContentView: View {
}
if root == .auth {
shouldPrepareAuthEntryAnimation = true
shouldPulseLaunchSplash = false
try? await Task.sleep(nanoseconds: 180_000_000)
withAnimation(.easeInOut(duration: 0.34)) {
showLaunchSplash = false
}
try? await Task.sleep(nanoseconds: 360_000_000)
authEntryAnimationToken += 1
scheduleDisableAuthEntryPreparation()
return
}
@@ -410,17 +402,13 @@ struct ContentView: View {
enterAuthFlow()
}
/// The only correct way to transition `root` to `.auth`: pairs the
/// switch with the token bump `LoginView` needs to run its entry
/// reveal animation. A bare `root = .auth` leaves the login screen's
/// hero/text/button stuck hidden - see
/// decisions/2026-08-06-login-entry-animation-bug.md.
/// The single funnel for entering the auth flow. `LoginView` is a
/// static screen with its own `LCENavigationView` back button (sets
/// `root = .main`), so no animation priming is needed here - see
/// decisions/2026-08-27-payment-auth-lockout-clean-intro-screen.md.
@MainActor
private func enterAuthFlow() {
shouldPrepareAuthEntryAnimation = true
root = .auth
authEntryAnimationToken += 1
scheduleDisableAuthEntryPreparation()
}
@MainActor
@@ -463,13 +451,6 @@ struct ContentView: View {
)
}
private func scheduleDisableAuthEntryPreparation() {
Task { @MainActor in
try? await Task.sleep(nanoseconds: 900_000_000)
shouldPrepareAuthEntryAnimation = false
}
}
private func hasConfiguredAddress() -> Bool {
if appState.address.selectedId != nil {
return true