| 1 | diff --git a/android/build.gradle b/android/build.gradle |
| 2 | index 6bd6d5acf6ac3da78232170a6ea8c2b0f6e5504e..a827dad53dcd8c1b2f47fce30ea8bf9140787f38 100644 |
| 3 | --- a/android/build.gradle |
| 4 | +++ b/android/build.gradle |
| 5 | @@ -146,6 +146,12 @@ dependencies { |
| 6 | implementation "androidx.activity:activity-compose:$activityComposeVersion" |
| 7 | implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycleVersion" |
| 8 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleVersion" |
| 9 | + // ClerkComposeNativeViewHost imports LocalSavedStateRegistryOwner and |
| 10 | + // LocalLifecycleOwner from these artifacts. Earlier SDKs leaked them onto |
| 11 | + // the compile classpath through clerk-android-ui's navigation3 runtime |
| 12 | + // dependency; AGP 9 resolves the compile classpath on its own. |
| 13 | + implementation "androidx.savedstate:savedstate-compose:1.4.0" |
| 14 | + implementation "androidx.lifecycle:lifecycle-runtime-compose:2.10.0" |
| 15 | |
| 16 | testImplementation "junit:junit:4.13.2" |
| 17 | } |
| 18 | diff --git a/ios/ClerkAuthNativeView.swift b/ios/ClerkAuthNativeView.swift |
| 19 | index 5f8d2ba66d5b232890af75c22b173c86d2ef546b..b170304cbf2fbaae8032db8dd6f89b6a59c6e3fe 100644 |
| 20 | --- a/ios/ClerkAuthNativeView.swift |
| 21 | +++ b/ios/ClerkAuthNativeView.swift |
| 22 | @@ -111,7 +111,12 @@ public class ClerkAuthNativeView: ClerkNativeViewHost { |
| 23 | |
| 24 | override func makeHostedController() -> UIViewController? { |
| 25 | let hostBackAction: (() -> Void)? = currentHostBackButton |
| 26 | - ? { [weak self] in self?.onHostBack([:]) } |
| 27 | + ? { [weak self] in |
| 28 | + guard let self else { return } |
| 29 | + if !self.popEnclosingNavigationRoute() { |
| 30 | + self.onHostBack([:]) |
| 31 | + } |
| 32 | + } |
| 33 | : nil |
| 34 | |
| 35 | return ClerkNativeBridge.shared.makeAuthViewController( |
| 36 | diff --git a/ios/ClerkNativeBridge.swift b/ios/ClerkNativeBridge.swift |
| 37 | index c90ad2ef1162cc2ac025c8b0b4aa135e8913dc21..ce8cb68764b1a3d3537672fc83e4e3077fceb97a 100644 |
| 38 | --- a/ios/ClerkNativeBridge.swift |
| 39 | +++ b/ios/ClerkNativeBridge.swift |
| 40 | @@ -1470,6 +1470,8 @@ private struct ClerkReactEmbeddedUserProfileCustomPage: View { |
| 41 | } |
| 42 | |
| 43 | private struct ClerkReactUserProfileCustomPageContent: View { |
| 44 | + @Environment(\.clerkTheme) private var theme |
| 45 | + |
| 46 | let path: String |
| 47 | let rows: [ClerkUserProfileCustomRowConfig] |
| 48 | let state: ClerkUserProfileCustomPageState |
| 49 | @@ -1483,8 +1485,13 @@ private struct ClerkReactUserProfileCustomPageContent: View { |
| 50 | } |
| 51 | } |
| 52 | .frame(maxWidth: .infinity, maxHeight: .infinity) |
| 53 | + // Match Clerk's own profile pages so the navigation bar and page share |
| 54 | + // the theme's page background instead of a bare hosting container. |
| 55 | + .background(theme.colors.muted) |
| 56 | .navigationTitle(userProfileCustomPageLabel(for: path, rows: rows)) |
| 57 | .navigationBarTitleDisplayMode(.inline) |
| 58 | + .preGlassSolidNavBar() |
| 59 | + .background(theme.colors.background) |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | diff --git a/ios/ClerkNativeViewHost.swift b/ios/ClerkNativeViewHost.swift |
| 64 | index 4e6540aceeaaab56ddf327bb3f16e120438f066f..cc16c94ca7a6eb91a97e76d5de02dc586f02a766 100644 |
| 65 | --- a/ios/ClerkNativeViewHost.swift |
| 66 | +++ b/ios/ClerkNativeViewHost.swift |
| 67 | @@ -5,6 +5,7 @@ public class ClerkNativeViewHost: ExpoView { |
| 68 | private lazy var hostingCoordinator = ClerkNativeHostingCoordinator(containerView: self) |
| 69 | private var hasInitialized: Bool = false |
| 70 | private var configuredObserver: NSObjectProtocol? |
| 71 | + private var isPoppingHostRoute = false |
| 72 | |
| 73 | public required init(appContext: AppContext? = nil) { |
| 74 | super.init(appContext: appContext) |
| 75 | @@ -58,6 +59,30 @@ public class ClerkNativeViewHost: ExpoView { |
| 76 | |
| 77 | func hostedViewDidDetachFromWindow() {} |
| 78 | |
| 79 | + /// Pops the React Navigation route that contains this view without waiting for |
| 80 | + /// the JavaScript event loop. React Native Screens reports the native dismissal |
| 81 | + /// back to React Navigation so its state remains synchronized. |
| 82 | + func popEnclosingNavigationRoute() -> Bool { |
| 83 | + guard !isPoppingHostRoute else { return true } |
| 84 | + |
| 85 | + var responder: UIResponder? = self |
| 86 | + |
| 87 | + while let nextResponder = responder?.next { |
| 88 | + if let viewController = nextResponder as? UIViewController, |
| 89 | + let navigationController = viewController.navigationController, |
| 90 | + navigationController.viewControllers.count > 1 { |
| 91 | + isPoppingHostRoute = true |
| 92 | + if navigationController.popViewController(animated: true) != nil { |
| 93 | + return true |
| 94 | + } |
| 95 | + isPoppingHostRoute = false |
| 96 | + } |
| 97 | + responder = nextResponder |
| 98 | + } |
| 99 | + |
| 100 | + return false |
| 101 | + } |
| 102 | + |
| 103 | private func addConfiguredObserver() { |
| 104 | guard configuredObserver == nil else { return } |
| 105 | |
| 106 | diff --git a/ios/ClerkUserProfileNativeView.swift b/ios/ClerkUserProfileNativeView.swift |
| 107 | index f2ca604d41f50d05de8ac334879126cdc0414f84..46f406d5361b9a560a7c4ce855e344cbddd1d0b6 100644 |
| 108 | --- a/ios/ClerkUserProfileNativeView.swift |
| 109 | +++ b/ios/ClerkUserProfileNativeView.swift |
| 110 | @@ -47,7 +47,12 @@ public class ClerkUserProfileNativeView: ClerkUserProfileCustomPageHost { |
| 111 | |
| 112 | override func makeHostedController() -> UIViewController? { |
| 113 | let hostBackAction: (() -> Void)? = currentHostBackButton |
| 114 | - ? { [weak self] in self?.onHostBack([:]) } |
| 115 | + ? { [weak self] in |
| 116 | + guard let self else { return } |
| 117 | + if !self.popEnclosingNavigationRoute() { |
| 118 | + self.onHostBack([:]) |
| 119 | + } |
| 120 | + } |
| 121 | : nil |
| 122 | |
| 123 | return ClerkNativeBridge.shared.makeUserProfileViewController( |