From 01e2e428c4aab4cdc877b16879dd09c1a3097dab Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Fri, 17 May 2024 10:05:50 -0400 Subject: [PATCH 01/14] Initial work to extract and expose a top-level navigator as the public interface for navigation --- .../dev/hotwire/core/bridge/BridgeDelegate.kt | 2 +- .../activities/HotwireActivityDelegate.kt | 61 ++++----- .../core/navigation/session/NavigatorHost.kt | 59 ++++++++ .../session/SessionNavHostFragment.kt | 111 --------------- .../turbo/delegates/TurboFragmentDelegate.kt | 14 +- .../delegates/TurboNestedFragmentDelegate.kt | 28 ++-- .../delegates/TurboWebFragmentDelegate.kt | 52 +++---- .../TurboBottomSheetDialogFragment.kt | 15 ++- .../core/turbo/fragments/TurboFragment.kt | 8 ++ .../TurboWebBottomSheetDialogFragment.kt | 6 +- .../core/turbo/fragments/TurboWebFragment.kt | 6 +- .../core/turbo/nav/HotwireNavDestination.kt | 40 +++--- .../turbo/nav/HotwireNavDialogDestination.kt | 38 ++++++ .../nav/{TurboNavigator.kt => Navigator.kt} | 127 ++++++++++++------ .../core/turbo/nav/TurboNavGraphBuilder.kt | 2 +- demo/src/main/res/layout/activity_main.xml | 2 +- 16 files changed, 303 insertions(+), 268 deletions(-) create mode 100644 core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt delete mode 100644 core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionNavHostFragment.kt create mode 100644 core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt rename core/src/main/kotlin/dev/hotwire/core/turbo/nav/{TurboNavigator.kt => Navigator.kt} (68%) diff --git a/core/src/main/kotlin/dev/hotwire/core/bridge/BridgeDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/bridge/BridgeDelegate.kt index 9bb5cd3..839a386 100644 --- a/core/src/main/kotlin/dev/hotwire/core/bridge/BridgeDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/bridge/BridgeDelegate.kt @@ -75,7 +75,7 @@ class BridgeDelegate( } private fun shouldReloadBridge(): Boolean { - return destination.session.isReady && bridge?.isReady() == false + return destination.navigator.session.isReady && bridge?.isReady() == false } // Lifecycle events diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt index fe46916..b9cfe1a 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt @@ -5,8 +5,8 @@ import androidx.activity.OnBackPressedCallback import androidx.annotation.IdRes import androidx.fragment.app.Fragment import androidx.navigation.NavController +import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.navigation.session.SessionConfiguration -import dev.hotwire.core.navigation.session.SessionNavHostFragment import dev.hotwire.core.turbo.nav.HotwireNavDestination import dev.hotwire.core.turbo.observers.HotwireActivityObserver import dev.hotwire.core.turbo.visit.VisitOptions @@ -16,12 +16,10 @@ import dev.hotwire.core.turbo.visit.VisitOptions * Activity to communicate with Hotwire Native (and vice versa). * * @property activity The Activity to bind this delegate to. - * @property currentNavHostFragmentId The resource ID of the [SessionNavHostFragment] - * instance hosted in your Activity's layout resource. */ @Suppress("unused", "MemberVisibilityCanBePrivate") class HotwireActivityDelegate(val activity: HotwireActivity) { - private val navHostFragments = mutableMapOf() + private val navigatorHosts = mutableMapOf() private val onBackPressedCallback = object : OnBackPressedCallback(enabled = true) { override fun handleOnBackPressed() { @@ -29,10 +27,10 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } } - private var currentNavHostFragmentId = activity.sessionConfigurations().first().navHostFragmentId + private var currentNavigatorHostId = activity.sessionConfigurations().first().navHostFragmentId set(value) { field = value - updateOnBackPressedCallback(currentNavHostFragment.navController) + updateOnBackPressedCallback(currentNavigatorHost.navController) } /** @@ -48,62 +46,61 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } /** - * Gets the Activity's currently active [SessionNavHostFragment]. + * Gets the Activity's currently active [NavigatorHost]. */ - val currentNavHostFragment: SessionNavHostFragment - get() = navHostFragment(currentNavHostFragmentId) + val currentNavigatorHost: NavigatorHost + get() = navigatorHost(currentNavigatorHostId) /** - * Gets the currently active Fragment destination hosted in the current - * [SessionNavHostFragment]. + * Gets the currently active destination hosted in the current [NavigatorHost]. */ val currentNavDestination: HotwireNavDestination? get() = currentFragment as HotwireNavDestination? /** * Sets the currently active session in your Activity. If you use multiple - * [SessionNavHostFragment] instances in your app (such as for bottom tabs), + * [NavigatorHost] instances in your app (such as for bottom tabs), * you must update this whenever the current session changes. */ fun setCurrentSession(sessionConfiguration: SessionConfiguration) { - currentNavHostFragmentId = sessionConfiguration.navHostFragmentId + currentNavigatorHostId = sessionConfiguration.navHostFragmentId } - internal fun registerNavHostFragment(navHostFragment: SessionNavHostFragment) { - if (navHostFragments[navHostFragment.id] == null) { - navHostFragments[navHostFragment.id] = navHostFragment - listenToDestinationChanges(navHostFragment.navController) + internal fun registerNavigatorHost(host: NavigatorHost) { + if (navigatorHosts[host.id] == null) { + navigatorHosts[host.id] = host + listenToDestinationChanges(host.navController) } } - internal fun unregisterNavHostFragment(navHostFragment: SessionNavHostFragment) { - navHostFragments.remove(navHostFragment.id) + internal fun unregisterNavigatorHost(host: NavigatorHost) { + navigatorHosts.remove(host.id) } /** - * Finds the nav host fragment associated with the provided resource ID. + * Finds the navigator host associated with the provided resource ID. * - * @param navHostFragmentId + * @param navigatorHostId * @return */ - fun navHostFragment(@IdRes navHostFragmentId: Int): SessionNavHostFragment { - return requireNotNull(navHostFragments[navHostFragmentId]) { - "No registered SessionNavHostFragment found" + fun navigatorHost(@IdRes navigatorHostId: Int): NavigatorHost { + return requireNotNull(navigatorHosts[navigatorHostId]) { + "No registered NavigatorHost found" } } /** - * Resets the Turbo sessions associated with all registered nav host fragments. + * Resets the sessions associated with all registered navigator hosts. */ fun resetSessions() { - navHostFragments.forEach { it.value.session.reset() } + navigatorHosts.forEach { it.value.navigator.session.reset() } } /** - * Resets all registered nav host fragments via [SessionNavHostFragment.reset]. + * Resets all registered navigators via [Navigator.reset]. */ - fun resetNavHostFragments() { - navHostFragments.forEach { it.value.reset() } + fun resetNavigators() { + navigatorHosts.forEach { it.value.navigator.reset() } } /** @@ -160,15 +157,15 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } private fun updateOnBackPressedCallback(navController: NavController) { - if (navController == currentNavHostFragment.navController) { + if (navController == currentNavigatorHost.navController) { onBackPressedCallback.isEnabled = navController.previousBackStackEntry != null } } private val currentFragment: Fragment? get() { - return if (currentNavHostFragment.isAdded && !currentNavHostFragment.isDetached) { - currentNavHostFragment.childFragmentManager.primaryNavigationFragment + return if (currentNavigatorHost.isAdded && !currentNavigatorHost.isDetached) { + currentNavigatorHost.childFragmentManager.primaryNavigationFragment } else { null } diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt new file mode 100644 index 0000000..9def1a6 --- /dev/null +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt @@ -0,0 +1,59 @@ +package dev.hotwire.core.navigation.session + +import android.content.Context +import android.os.Bundle +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController +import dev.hotwire.core.config.Hotwire +import dev.hotwire.core.config.Hotwire.pathConfiguration +import dev.hotwire.core.navigation.activities.HotwireActivity +import dev.hotwire.core.turbo.nav.Navigator +import dev.hotwire.core.turbo.nav.TurboNavGraphBuilder +import dev.hotwire.core.turbo.session.Session +import dev.hotwire.core.turbo.views.TurboWebView + +open class NavigatorHost : NavHostFragment() { + internal lateinit var activity: HotwireActivity + lateinit var navigator: Navigator + private set + + val sessionConfiguration get() = activity.sessionConfigurations().first { + id == it.navHostFragmentId + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity = requireActivity() as HotwireActivity + activity.delegate.registerNavigatorHost(this) + navigator = Navigator(this) + + initControllerGraph() + } + + override fun onDestroy() { + super.onDestroy() + activity.delegate.unregisterNavigatorHost(this) + } + + /** + * Called whenever a new WebView instance needs to be (re)created. You can + * override this to provide your own [TurboWebView] subclass if you need + * custom behaviors. + */ + open fun onCreateWebView(context: Context): TurboWebView { + return TurboWebView(context, null) + } + + internal fun initControllerGraph() { + navController.apply { + graph = TurboNavGraphBuilder( + startLocation = sessionConfiguration.startLocation, + pathConfiguration = pathConfiguration, + navController = findNavController() + ).build( + registeredFragments = Hotwire.registeredFragmentDestinations + ) + } + } +} diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionNavHostFragment.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionNavHostFragment.kt deleted file mode 100644 index 0a1db70..0000000 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionNavHostFragment.kt +++ /dev/null @@ -1,111 +0,0 @@ -package dev.hotwire.core.navigation.session - -import android.content.Context -import android.os.Bundle -import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.fragment.findNavController -import dev.hotwire.core.bridge.Bridge -import dev.hotwire.core.config.Hotwire -import dev.hotwire.core.config.Hotwire.pathConfiguration -import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.nav.TurboNavGraphBuilder -import dev.hotwire.core.turbo.session.Session -import dev.hotwire.core.turbo.views.TurboWebView - -open class SessionNavHostFragment : NavHostFragment() { - private val activity get() = requireActivity() as HotwireActivity - - val sessionConfiguration get() = activity.sessionConfigurations().first { - id == it.navHostFragmentId - } - - /** - * The [Session] instance that is shared with all destinations that are - * hosted inside this [SessionNavHostFragment]. - */ - lateinit var session: Session - private set - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - activity.delegate.registerNavHostFragment(this) - - createNewSession() - initControllerGraph() - } - - override fun onDestroy() { - super.onDestroy() - activity.delegate.unregisterNavHostFragment(this) - } - - internal fun createNewSession() { - session = Session( - sessionName = sessionConfiguration.name, - activity = activity, - webView = onCreateWebView(activity) - ) - onSessionCreated() - } - - /** - * Called whenever the [Session] instance has been (re)created. A new - * session is created whenever the [SessionNavHostFragment] is created - * and whenever the WebView render process has been terminated and a new - * WebView instance is required. - */ - open fun onSessionCreated() { - // Initialize bridge with new WebView instance - if (Hotwire.registeredBridgeComponentFactories.isNotEmpty()) { - Bridge.initialize(session.webView) - } - } - - /** - * Called whenever a new WebView instance needs to be (re)created. You can - * override this to provide your own [TurboWebView] subclass if you need - * custom behaviors. - */ - open fun onCreateWebView(context: Context): TurboWebView { - return TurboWebView(context, null) - } - - /** - * Resets the [SessionNavHostFragment] instance, it's [Session] - * instance, and the entire navigation graph to its original starting point. - */ - fun reset(onReset: () -> Unit = {}) { - currentNavDestination.delegate().navigator.onNavigationVisit { - currentNavDestination.clearBackStack { - session.reset() - initControllerGraph() - - if (view == null) { - onReset() - } else { - requireView().post { onReset() } - } - } - } - } - - /** - * Retrieves the currently active [HotwireNavDestination] on the backstack. - */ - val currentNavDestination: HotwireNavDestination - get() = childFragmentManager.primaryNavigationFragment as HotwireNavDestination? - ?: throw IllegalStateException("No current destination found in NavHostFragment") - - private fun initControllerGraph() { - navController.apply { - graph = TurboNavGraphBuilder( - startLocation = sessionConfiguration.startLocation, - pathConfiguration = pathConfiguration, - navController = findNavController() - ).build( - registeredFragments = Hotwire.registeredFragmentDestinations - ) - } - } -} diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt index fe978d3..5e31ab9 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt @@ -3,7 +3,7 @@ package dev.hotwire.core.turbo.delegates import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.turbo.fragments.TurboFragmentViewModel import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.nav.TurboNavigator +import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.session.SessionModalResult import dev.hotwire.core.turbo.session.SessionViewModel import dev.hotwire.core.turbo.util.displayBackButton @@ -17,20 +17,20 @@ import dev.hotwire.core.turbo.util.displayBackButtonAsCloseIcon class TurboFragmentDelegate(private val navDestination: HotwireNavDestination) { private val fragment = navDestination.fragment private val location = navDestination.location - private val sessionName = navDestination.session.sessionName + private val navigator = navDestination.navigator - internal val sessionViewModel = SessionViewModel.get(sessionName, fragment.requireActivity()) + internal val sessionViewModel = SessionViewModel.get(navigator.session.sessionName, fragment.requireActivity()) internal val fragmentViewModel = TurboFragmentViewModel.get(location, fragment) - internal lateinit var navigator: TurboNavigator + fun prepareNavigation(onReady: () -> Unit) { + onReady() + } /** * Should be called by the implementing Fragment during * [androidx.fragment.app.Fragment.onViewCreated]. */ fun onViewCreated() { - navigator = TurboNavigator(navDestination) - initToolbar() logEvent("fragment.onViewCreated", "location" to location) } @@ -110,7 +110,7 @@ class TurboFragmentDelegate(private val navDestination: HotwireNavDestination) { private fun logEvent(event: String, vararg params: Pair) { val attributes = params.toMutableList().apply { - add(0, "session" to sessionName) + add(0, "session" to navigator.session.sessionName) add("fragment" to fragment.javaClass.simpleName) } logEvent(event, attributes) diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt index cdf5d39..d48e44f 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt @@ -4,12 +4,12 @@ import android.os.Bundle import androidx.annotation.IdRes import androidx.fragment.app.Fragment import androidx.navigation.NavController -import dev.hotwire.core.navigation.session.SessionNavHostFragment +import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.turbo.nav.HotwireNavDestination import dev.hotwire.core.turbo.visit.VisitOptions /** - * A simplified delegate that can be used when a [SessionNavHostFragment] is nested + * A simplified delegate that can be used when a [NavigatorHost] is nested * within a Fragment. This can be useful when you want a portion of the screen to have * sub-navigation destinations within the current Fragment. * @@ -17,28 +17,28 @@ import dev.hotwire.core.turbo.visit.VisitOptions * results load in a section of the view below the search bar. * * @property fragment The Fragment to bind this delegate to. - * @param navHostFragmentId The resource ID of the [SessionNavHostFragment] - * instance hosted in your Fragment's layout resource. + * @param navigatorHostId The resource ID of the [NavigatorHost] + * instance hosted in your Activity's layout resource. */ @Suppress("unused", "MemberVisibilityCanBePrivate") -class TurboNestedFragmentDelegate(val fragment: Fragment, navHostFragmentId: Int) { - val navHostFragment by lazy { findNavHostFragment(navHostFragmentId) } +class TurboNestedFragmentDelegate(val fragment: Fragment, navigatorHostId: Int) { + val navigatorHost by lazy { findNavigatorHost(navigatorHostId) } val currentNavDestination: HotwireNavDestination get() = currentFragment as HotwireNavDestination /** - * Resets the nav host fragment via [SessionNavHostFragment.reset] + * Resets the navigator via [NavigatorHost.navigator.reset] */ - fun resetNavHostFragment() { - navHostFragment.reset() + fun resetNavigator() { + navigatorHost.navigator.reset() } /** * Resets the Turbo session associated with the nav host fragment. */ fun resetSession() { - navHostFragment.session.reset() + navigatorHost.navigator.session.reset() } /** @@ -81,10 +81,10 @@ class TurboNestedFragmentDelegate(val fragment: Fragment, navHostFragmentId: Int } private val currentFragment: Fragment - get() = navHostFragment.childFragmentManager.primaryNavigationFragment as Fragment + get() = navigatorHost.childFragmentManager.primaryNavigationFragment as Fragment - private fun findNavHostFragment(@IdRes navHostFragmentId: Int): SessionNavHostFragment { - return fragment.childFragmentManager.findFragmentById(navHostFragmentId) as? SessionNavHostFragment - ?: throw IllegalStateException("No SessionNavHostFragment found with ID: $navHostFragmentId") + private fun findNavigatorHost(@IdRes navHostFragmentId: Int): NavigatorHost { + return fragment.childFragmentManager.findFragmentById(navHostFragmentId) as? NavigatorHost + ?: throw IllegalStateException("No NavigatorHost found with ID: $navHostFragmentId") } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt index 0a109f8..cb68266 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt @@ -13,8 +13,6 @@ import dev.hotwire.core.turbo.config.pullToRefreshEnabled import dev.hotwire.core.turbo.errors.VisitError import dev.hotwire.core.turbo.fragments.TurboWebFragmentCallback import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.nav.TurboNavigator -import dev.hotwire.core.turbo.session.Session import dev.hotwire.core.turbo.session.SessionCallback import dev.hotwire.core.turbo.session.SessionModalResult import dev.hotwire.core.turbo.util.dispatcherProvider @@ -46,37 +44,34 @@ internal class TurboWebFragmentDelegate( private var screenshotOrientation = 0 private var screenshotZoomed = false private var currentlyZoomed = false - private val navigator: TurboNavigator - get() = navDestination.delegate().navigator - private val turboView: TurboView? - get() = callback.turboView - private val viewTreeLifecycleOwner - get() = turboView?.findViewTreeLifecycleOwner() + private val navigator = navDestination.navigator + private val session get() = navigator.session + private val turboView get() = callback.turboView + private val viewTreeLifecycleOwner get() = turboView?.findViewTreeLifecycleOwner() /** * Get the session's WebView instance */ val webView: TurboWebView - get() = session().webView + get() = session.webView /** * The activity result launcher that handles file chooser results. */ val fileChooserResultLauncher = registerFileChooserLauncher() + fun prepareNavigation(onReady: () -> Unit) { + session.removeCallback(this) + detachWebView(onReady) + } + /** * Should be called by the implementing Fragment during * [androidx.fragment.app.Fragment.onViewCreated]. */ fun onViewCreated() { - if (session().isRenderProcessGone) { - navDestination.sessionNavHostFragment.createNewSession() - } - - navigator.onNavigationVisit = { onReady -> - navDestination.onBeforeNavigation() - session().removeCallback(this) - detachWebView(onReady) + if (session.isRenderProcessGone) { + navigator.createNewSession() } } @@ -115,7 +110,7 @@ internal class TurboWebFragmentDelegate( * before navigation. */ fun onDialogCancel() { - session().removeCallback(this) + session.removeCallback(this) detachWebView() } @@ -127,7 +122,7 @@ internal class TurboWebFragmentDelegate( // The WebView is already detached in most circumstances, but sometimes // fast user cancellation does not call onCancel() before onDismiss() if (webViewIsAttached()) { - session().removeCallback(this) + session.removeCallback(this) detachWebView() } } @@ -149,13 +144,6 @@ internal class TurboWebFragmentDelegate( visit(location, restoreWithCachedSnapshot = false, reload = true) } - /** - * Retrieves the Turbo session from the destination. - */ - fun session(): Session { - return navDestination.session - } - /** * Displays the error view that's implemented via [TurboWebFragmentCallback.createErrorView]. */ @@ -316,8 +304,8 @@ internal class TurboWebFragmentDelegate( // Visit every time the WebView is reattached to the current Fragment. if (isWebViewAttachedToNewDestination) { val currentSessionVisitRestored = !isInitialVisit && - session().currentVisit?.destinationIdentifier == identifier && - session().restoreCurrentVisit(this) + session.currentVisit?.destinationIdentifier == identifier && + session.restoreCurrentVisit(this) if (!currentSessionVisitRestored) { showProgressView(location) @@ -339,7 +327,7 @@ internal class TurboWebFragmentDelegate( private fun registerFileChooserLauncher(): ActivityResultLauncher { return navDestination.fragment.registerForActivityResult(StartActivityForResult()) { result -> - session().fileChooserDelegate.onActivityResult(result) + session.fileChooserDelegate.onActivityResult(result) } } @@ -358,7 +346,7 @@ internal class TurboWebFragmentDelegate( } viewTreeLifecycleOwner?.lifecycle?.whenStateAtLeast(STARTED) { - session().visit( + session.visit( Visit( location = location, destinationIdentifier = identifier, @@ -374,7 +362,7 @@ internal class TurboWebFragmentDelegate( private suspend fun fetchCachedSnapshot(): String? { return withContext(dispatcherProvider.io) { - val response = session().offlineRequestHandler?.getCachedSnapshot( + val response = session.offlineRequestHandler?.getCachedSnapshot( url = location ) @@ -385,7 +373,7 @@ internal class TurboWebFragmentDelegate( } private fun screenshotView() { - if (!session().screenshotsEnabled) return + if (!session.screenshotsEnabled) return turboView?.let { screenshot = it.createScreenshot() diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt index d182fb6..ac08f78 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt @@ -7,9 +7,12 @@ import android.view.View import androidx.appcompat.widget.Toolbar import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dev.hotwire.core.R +import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.turbo.config.title import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate import dev.hotwire.core.turbo.nav.HotwireNavDestination +import dev.hotwire.core.turbo.nav.HotwireNavDialogDestination +import dev.hotwire.core.turbo.nav.Navigator /** * The base class from which all bottom sheet native fragments in a @@ -18,11 +21,13 @@ import dev.hotwire.core.turbo.nav.HotwireNavDestination * For web bottom sheet fragments, refer to [TurboWebBottomSheetDialogFragment]. */ abstract class TurboBottomSheetDialogFragment : BottomSheetDialogFragment(), - HotwireNavDestination { + HotwireNavDestination, HotwireNavDialogDestination { + override lateinit var navigator: Navigator internal lateinit var delegate: TurboFragmentDelegate override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + navigator = (parentFragment as NavigatorHost).navigator delegate = TurboFragmentDelegate(this) } @@ -85,10 +90,18 @@ abstract class TurboBottomSheetDialogFragment : BottomSheetDialogFragment(), super.onDismiss(dialog) } + override fun closeDialog() { + requireDialog().cancel() + } + override fun onBeforeNavigation() {} override fun refresh(displayProgress: Boolean) {} + override fun prepareNavigation(onReady: () -> Unit) { + delegate.prepareNavigation(onReady) + } + /** * Gets the Toolbar instance in your Fragment's view for use with * navigation. The title in the Toolbar will automatically be diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt index bf42eb8..6542b4c 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt @@ -6,10 +6,12 @@ import android.view.View import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import dev.hotwire.core.R +import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.turbo.config.context import dev.hotwire.core.turbo.config.title import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate import dev.hotwire.core.turbo.nav.HotwireNavDestination +import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.nav.TurboNavPresentationContext import dev.hotwire.core.turbo.observers.HotwireWindowThemeObserver import dev.hotwire.core.turbo.session.SessionModalResult @@ -21,10 +23,12 @@ import dev.hotwire.core.turbo.session.SessionModalResult * For web fragments, refer to [TurboWebFragment]. */ abstract class TurboFragment : Fragment(), HotwireNavDestination { + override lateinit var navigator: Navigator internal lateinit var delegate: TurboFragmentDelegate override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + navigator = (parentFragment as NavigatorHost).navigator delegate = TurboFragmentDelegate(this) } @@ -84,6 +88,10 @@ abstract class TurboFragment : Fragment(), HotwireNavDestination { delegate.onStop() } + override fun prepareNavigation(onReady: () -> Unit) { + delegate.prepareNavigation(onReady) + } + /** * Called when the Fragment has been started again after receiving a * modal result. Will navigate if the result indicates it should. diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebBottomSheetDialogFragment.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebBottomSheetDialogFragment.kt index 37e3138..e60c4e2 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebBottomSheetDialogFragment.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebBottomSheetDialogFragment.kt @@ -65,6 +65,10 @@ abstract class TurboWebBottomSheetDialogFragment : TurboBottomSheetDialogFragmen webDelegate.refresh(displayProgress) } + final override fun prepareNavigation(onReady: () -> Unit) { + webDelegate.prepareNavigation(onReady) + } + // ---------------------------------------------------------------------------- // TurboWebFragmentCallback interface // ---------------------------------------------------------------------------- @@ -87,7 +91,7 @@ abstract class TurboWebBottomSheetDialogFragment : TurboBottomSheetDialogFragmen } override fun createWebChromeClient(): TurboWebChromeClient { - return TurboWebChromeClient(session) + return TurboWebChromeClient(navigator.session) } override fun onVisitErrorReceived(location: String, error: VisitError) { diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebFragment.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebFragment.kt index 035c567..5c04288 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebFragment.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboWebFragment.kt @@ -78,6 +78,10 @@ abstract class TurboWebFragment : TurboFragment(), TurboWebFragmentCallback { } } + final override fun prepareNavigation(onReady: () -> Unit) { + webDelegate.prepareNavigation(onReady) + } + // ---------------------------------------------------------------------------- // TurboWebFragmentCallback interface // ---------------------------------------------------------------------------- @@ -100,7 +104,7 @@ abstract class TurboWebFragment : TurboFragment(), TurboWebFragmentCallback { } override fun createWebChromeClient(): TurboWebChromeClient { - return TurboWebChromeClient(session) + return TurboWebChromeClient(navigator.session) } override fun onVisitErrorReceived(location: String, error: VisitError) { diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt index 9fbebb3..caf5bad 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt @@ -17,7 +17,7 @@ import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.navigation.activities.HotwireActivity import dev.hotwire.core.navigation.routing.Router -import dev.hotwire.core.navigation.session.SessionNavHostFragment +import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.turbo.config.PathConfigurationProperties import dev.hotwire.core.turbo.config.context import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate @@ -25,7 +25,6 @@ import dev.hotwire.core.turbo.delegates.TurboNestedFragmentDelegate import dev.hotwire.core.turbo.fragments.TurboFragment import dev.hotwire.core.turbo.fragments.TurboFragmentViewModel import dev.hotwire.core.turbo.fragments.TurboWebFragment -import dev.hotwire.core.turbo.session.Session import dev.hotwire.core.turbo.visit.VisitAction import dev.hotwire.core.turbo.visit.VisitOptions @@ -35,16 +34,15 @@ import dev.hotwire.core.turbo.visit.VisitOptions */ interface HotwireNavDestination { /** - * Gets the fragment instance for this destination. + * Gets the navigator instance associated with this destination. */ - val fragment: Fragment - get() = this as Fragment + val navigator: Navigator /** - * Gets the Turbo session's nav host fragment associated with this destination. + * Gets the fragment instance for this destination. */ - val sessionNavHostFragment: SessionNavHostFragment - get() = fragment.parentFragment as SessionNavHostFragment + val fragment: Fragment + get() = this as Fragment /** * Gets the location for this destination. @@ -65,12 +63,6 @@ interface HotwireNavDestination { val pathProperties: PathConfigurationProperties get() = pathConfiguration.properties(location) - /** - * Gets the [Session] associated with this destination. - */ - val session: Session - get() = sessionNavHostFragment.session - /** * Gets the [TurboFragmentViewModel] associated with this destination. */ @@ -129,8 +121,9 @@ interface HotwireNavDestination { * not have to override this, unless you're using a [TurboNestedFragmentDelegate] to provide * sub-navigation within your current Fragment destination and would like custom behavior. */ - fun navHostForNavigation(newLocation: String): SessionNavHostFragment { - return sessionNavHostFragment + // TODO replace with `navigatorForNavigation()` + fun navHostForNavigation(newLocation: String): NavigatorHost { + return navigator.host } /** @@ -142,7 +135,7 @@ interface HotwireNavDestination { fun route(newLocation: String): Router.RouteResult { return Hotwire.router.route( location = newLocation, - sessionConfiguration = sessionNavHostFragment.sessionConfiguration, + sessionConfiguration = navigator.host.sessionConfiguration, activity = fragment.requireActivity() as HotwireActivity ) } @@ -239,19 +232,18 @@ interface HotwireNavDestination { /** * Finds the nav host fragment with the given resource ID. */ - fun findNavHostFragment(@IdRes navHostFragmentId: Int): SessionNavHostFragment { + fun findNavHostFragment(@IdRes navHostFragmentId: Int): NavigatorHost { return fragment.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) ?: fragment.parentFragment?.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) ?: fragment.requireActivity().supportFragmentManager.findNavHostFragment(navHostFragmentId) - ?: throw IllegalStateException("No SessionNavHostFragment found with ID: $navHostFragmentId") + ?: throw IllegalStateException("No NavigatorHost found with ID: $navHostFragmentId") } + fun prepareNavigation(onReady: () -> Unit) + private val Bundle.location get() = getString("location") - private val navigator: TurboNavigator - get() = delegate().navigator - /** * Retrieve the nav controller indirectly from the parent NavHostFragment, * since it's only available when the fragment is attached to its parent. @@ -260,7 +252,7 @@ interface HotwireNavDestination { return fragment.parentFragment?.findNavController() } - private fun FragmentManager.findNavHostFragment(navHostFragmentId: Int): SessionNavHostFragment? { - return findFragmentById(navHostFragmentId) as? SessionNavHostFragment + private fun FragmentManager.findNavHostFragment(navHostFragmentId: Int): NavigatorHost? { + return findFragmentById(navHostFragmentId) as? NavigatorHost } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt new file mode 100644 index 0000000..9835559 --- /dev/null +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt @@ -0,0 +1,38 @@ +package dev.hotwire.core.turbo.nav + +import android.content.Intent +import android.os.Bundle +import androidx.activity.result.ActivityResultLauncher +import androidx.annotation.IdRes +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.navigation.NavController +import androidx.navigation.NavOptions +import androidx.navigation.fragment.FragmentNavigator +import androidx.navigation.fragment.findNavController +import androidx.navigation.navOptions +import dev.hotwire.core.R +import dev.hotwire.core.config.Hotwire +import dev.hotwire.core.config.Hotwire.pathConfiguration +import dev.hotwire.core.navigation.activities.HotwireActivity +import dev.hotwire.core.navigation.routing.Router +import dev.hotwire.core.navigation.session.NavigatorHost +import dev.hotwire.core.turbo.config.PathConfigurationProperties +import dev.hotwire.core.turbo.config.context +import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate +import dev.hotwire.core.turbo.delegates.TurboNestedFragmentDelegate +import dev.hotwire.core.turbo.fragments.TurboFragment +import dev.hotwire.core.turbo.fragments.TurboFragmentViewModel +import dev.hotwire.core.turbo.fragments.TurboWebFragment +import dev.hotwire.core.turbo.session.Session +import dev.hotwire.core.turbo.visit.VisitAction +import dev.hotwire.core.turbo.visit.VisitOptions + +/** + * The interface that a navigable DialogFragment implements to provide the library with + * the information it needs to properly navigate. + */ +interface HotwireNavDialogDestination { + fun closeDialog() +} diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavigator.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt similarity index 68% rename from core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavigator.kt rename to core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt index e5457b0..f85406a 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt @@ -1,48 +1,71 @@ package dev.hotwire.core.turbo.nav import android.os.Bundle -import androidx.fragment.app.DialogFragment import androidx.navigation.NavBackStackEntry import androidx.navigation.NavController import androidx.navigation.NavOptions import androidx.navigation.fragment.FragmentNavigator -import androidx.navigation.fragment.findNavController +import dev.hotwire.core.bridge.Bridge +import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.navigation.routing.Router +import dev.hotwire.core.navigation.session.NavigatorHost +import dev.hotwire.core.turbo.session.Session import dev.hotwire.core.turbo.util.location import dev.hotwire.core.turbo.visit.VisitAction import dev.hotwire.core.turbo.visit.VisitOptions -internal class TurboNavigator(private val navDestination: HotwireNavDestination) { - private val fragment = navDestination.fragment - private val session = navDestination.session - - var onNavigationVisit: (onNavigate: () -> Unit) -> Unit = { onReady -> - navDestination.onBeforeNavigation() - onReady() +class Navigator(val host: NavigatorHost) { + private val navController = host.navController + + /** + * Retrieves the currently active [HotwireNavDestination] on the backstack. + */ + val currentDestination: HotwireNavDestination + get() = host.childFragmentManager.primaryNavigationFragment as HotwireNavDestination? + ?: throw IllegalStateException("No current destination found in NavHostFragment") + + /** + * The [Session] instance that is shared with all destinations that are + * hosted inside this [NavigatorHost]. + */ + var session = createNewSession() + private set + + internal fun createNewSession() = Session( + sessionName = host.sessionConfiguration.name, + activity = host.activity, + webView = host.onCreateWebView(host.activity) + ).also { + // Initialize bridge with new WebView instance + if (Hotwire.registeredBridgeComponentFactories.isNotEmpty()) { + Bridge.initialize(it.webView) + } } fun isAtStartDestination(): Boolean { - return currentController().previousBackStackEntry == null + return navController.previousBackStackEntry == null } fun navigateUp() { - onNavigationVisit { - if (fragment is DialogFragment) { - fragment.requireDialog().cancel() + navigateWhenReady { + val currentFragment = currentDestination.fragment + if (currentFragment is HotwireNavDialogDestination) { + currentFragment.closeDialog() } else { - currentController().navigateUp() + navController.navigateUp() } } } fun navigateBack() { - onNavigationVisit { - if (fragment is DialogFragment) { - fragment.requireDialog().cancel() + navigateWhenReady { + val currentFragment = currentDestination.fragment + if (currentFragment is HotwireNavDialogDestination) { + currentFragment.closeDialog() } else { - currentController().popBackStack() + navController.popBackStack() } } } @@ -101,17 +124,41 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) return } - onNavigationVisit { - if (fragment is DialogFragment) { - fragment.requireDialog().cancel() + navigateWhenReady { + val currentFragment = currentDestination.fragment + if (currentFragment is HotwireNavDialogDestination) { + currentFragment.closeDialog() } - val controller = currentController() - controller.popBackStack(controller.graph.startDestinationId, false) + navController.popBackStack(navController.graph.startDestinationId, false) onCleared() } } + /** + * Resets the [Navigator] along with its [NavigatorHost] and [Session] instances. + * The entire navigation graph is reset to its original starting point. + */ + fun reset(onReset: () -> Unit = {}) { + navigateWhenReady { + clearBackStack { + session.reset() + host.initControllerGraph() + + if (host.view == null) { + onReset() + } else { + host.requireView().post { onReset() } + } + } + } + } + + private fun navigateWhenReady(onReady: () -> Unit) { + currentDestination.onBeforeNavigation() + currentDestination.prepareNavigation(onReady) + } + private fun navigateWithinContext(rule: TurboNavRule) { logEvent( "navigateWithinContext", @@ -120,20 +167,20 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) ) when (rule.newPresentation) { - TurboNavPresentation.POP -> onNavigationVisit { + TurboNavPresentation.POP -> navigateWhenReady { popBackStack(rule) } - TurboNavPresentation.REPLACE -> onNavigationVisit { + TurboNavPresentation.REPLACE -> navigateWhenReady { popBackStack(rule) navigateToLocation(rule) } - TurboNavPresentation.PUSH -> onNavigationVisit { + TurboNavPresentation.PUSH -> navigateWhenReady { navigateToLocation(rule) } - TurboNavPresentation.REPLACE_ROOT -> onNavigationVisit { + TurboNavPresentation.REPLACE_ROOT -> navigateWhenReady { replaceRootLocation(rule) } - TurboNavPresentation.CLEAR_ALL -> onNavigationVisit { + TurboNavPresentation.CLEAR_ALL -> navigateWhenReady { clearBackStack() } else -> { @@ -149,11 +196,11 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) ) when (rule.newPresentation) { - TurboNavPresentation.REPLACE -> onNavigationVisit { + TurboNavPresentation.REPLACE -> navigateWhenReady { popBackStack(rule) navigateToLocation(rule) } - else -> onNavigationVisit { + else -> navigateWhenReady { navigateToLocation(rule) } } @@ -167,8 +214,8 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) "presentation" to rule.newPresentation ) - onNavigationVisit { - val isDialog = fragment is DialogFragment + navigateWhenReady { + val isDialog = currentDestination.fragment is HotwireNavDialogDestination if (isDialog) { // Pop the backstack before sending the modal result, since the // underlying fragment is still active and will receive the @@ -202,7 +249,7 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) private fun sendModalResult(rule: TurboNavRule) { // Save the modal result with VisitOptions so it can be retrieved // by the previous destination when the backstack is popped. - navDestination.delegate().sessionViewModel.sendModalResult( + currentDestination.delegate().sessionViewModel.sendModalResult( checkNotNull(rule.newModalResult) ) } @@ -230,7 +277,7 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) // Save the VisitOptions so it can be retrieved by the next // destination. When response.responseHTML is present it is // too large to save directly within the args bundle. - navDestination.delegate().sessionViewModel.saveVisitOptions(rule.newVisitOptions) + currentDestination.delegate().sessionViewModel.saveVisitOptions(rule.newVisitOptions) rule.newDestination?.let { logEvent( @@ -266,16 +313,12 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) ) } - private fun currentController(): NavController { - return fragment.findNavController() - } - private fun currentControllerForLocation(location: String): NavController { - return navDestination.navHostForNavigation(location).navController + return currentDestination.navHostForNavigation(location).navController } private fun getRouteResult(location: String): Router.RouteResult { - val result = navDestination.route(location) + val result = currentDestination.route(location) logEvent( "routeResult", @@ -288,7 +331,7 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) private fun navOptions(location: String, action: VisitAction): NavOptions { val properties = pathConfiguration.properties(location) - return navDestination.getNavigationOptions( + return currentDestination.getNavigationOptions( newLocation = location, newPathProperties = properties, action = action @@ -304,7 +347,7 @@ internal class TurboNavigator(private val navDestination: HotwireNavDestination) private fun logEvent(event: String, vararg params: Pair) { val attributes = params.toMutableList().apply { add(0, "session" to session.sessionName) - add("currentFragment" to fragment.javaClass.simpleName) + add("currentFragment" to currentDestination.fragment.javaClass.simpleName) } logEvent(event, attributes) } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt index b9e8e9b..1fa9c21 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt @@ -69,7 +69,7 @@ internal class TurboNavGraphBuilder( // Use a random value to represent a unique instance of the graph, so the // graph is unique every time. This lets it be reset/recreated on-demand from - // `SessionNavHostFragment.reset()`. Replacing an existing nav graph with + // `NavigatorHost.reset()`. Replacing an existing nav graph with // an identical one would bypass recreating the nav stack from scratch in // `NavController.setGraph()`. argument("unique_instance") { diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml index 7fe08a1..aa2ad1e 100644 --- a/demo/src/main/res/layout/activity_main.xml +++ b/demo/src/main/res/layout/activity_main.xml @@ -9,7 +9,7 @@ From 474050317571c28ed54fef5635d92e6945d9e5ce Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Mon, 20 May 2024 12:51:50 -0400 Subject: [PATCH 02/14] Allow consumer to create a custom WebView instance through a top-level config option instead of subclassing the nav host --- .../kotlin/dev/hotwire/core/config/HotwireConfig.kt | 11 +++++++++++ .../hotwire/core/navigation/session/NavigatorHost.kt | 12 ------------ .../kotlin/dev/hotwire/core/turbo/nav/Navigator.kt | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/core/src/main/kotlin/dev/hotwire/core/config/HotwireConfig.kt b/core/src/main/kotlin/dev/hotwire/core/config/HotwireConfig.kt index 2e3e7dc..7bb7a24 100644 --- a/core/src/main/kotlin/dev/hotwire/core/config/HotwireConfig.kt +++ b/core/src/main/kotlin/dev/hotwire/core/config/HotwireConfig.kt @@ -1,8 +1,10 @@ package dev.hotwire.core.config +import android.content.Context import android.webkit.WebView import dev.hotwire.core.bridge.StradaJsonConverter import dev.hotwire.core.turbo.http.TurboHttpClient +import dev.hotwire.core.turbo.views.TurboWebView class HotwireConfig internal constructor() { /** @@ -36,6 +38,15 @@ class HotwireConfig internal constructor() { WebView.setWebContentsDebuggingEnabled(value) } + /** + * Called whenever a new WebView instance needs to be (re)created. Provide + * your own implementation and subclass [TurboWebView] if you need + * custom behaviors. + */ + var makeCustomWebView: (context: Context) -> TurboWebView = { context -> + TurboWebView(context, null) + } + /** * Provides a standard substring to be included in your WebView's user agent * to identify itself as a Hotwire Native app. diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt index 9def1a6..4e15caa 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt @@ -1,6 +1,5 @@ package dev.hotwire.core.navigation.session -import android.content.Context import android.os.Bundle import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController @@ -9,8 +8,6 @@ import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.navigation.activities.HotwireActivity import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.nav.TurboNavGraphBuilder -import dev.hotwire.core.turbo.session.Session -import dev.hotwire.core.turbo.views.TurboWebView open class NavigatorHost : NavHostFragment() { internal lateinit var activity: HotwireActivity @@ -36,15 +33,6 @@ open class NavigatorHost : NavHostFragment() { activity.delegate.unregisterNavigatorHost(this) } - /** - * Called whenever a new WebView instance needs to be (re)created. You can - * override this to provide your own [TurboWebView] subclass if you need - * custom behaviors. - */ - open fun onCreateWebView(context: Context): TurboWebView { - return TurboWebView(context, null) - } - internal fun initControllerGraph() { navController.apply { graph = TurboNavGraphBuilder( diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt index f85406a..dd17551 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt @@ -36,7 +36,7 @@ class Navigator(val host: NavigatorHost) { internal fun createNewSession() = Session( sessionName = host.sessionConfiguration.name, activity = host.activity, - webView = host.onCreateWebView(host.activity) + webView = Hotwire.config.makeCustomWebView(host.requireContext()) ).also { // Initialize bridge with new WebView instance if (Hotwire.registeredBridgeComponentFactories.isNotEmpty()) { From 9d85407b862839a9a600196c390a5a34c02e1a44 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Mon, 20 May 2024 14:59:06 -0400 Subject: [PATCH 03/14] Remove the public navigation APIs from the destination interface and defer to the Navigator instead --- .../activities/HotwireActivityDelegate.kt | 42 +++++------ .../turbo/delegates/TurboFragmentDelegate.kt | 2 +- .../delegates/TurboNestedFragmentDelegate.kt | 65 +---------------- .../core/turbo/nav/HotwireNavDestination.kt | 69 ------------------- .../dev/hotwire/core/turbo/nav/Navigator.kt | 51 +++++++++++++- .../demo/features/numbers/NumbersFragment.kt | 2 +- .../hotwire/demo/features/web/WebFragment.kt | 2 +- 7 files changed, 74 insertions(+), 159 deletions(-) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt index b9cfe1a..5ffa4c7 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt @@ -3,11 +3,11 @@ package dev.hotwire.core.navigation.activities import android.os.Bundle import androidx.activity.OnBackPressedCallback import androidx.annotation.IdRes -import androidx.fragment.app.Fragment import androidx.navigation.NavController import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.navigation.session.SessionConfiguration import dev.hotwire.core.turbo.nav.HotwireNavDestination +import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.observers.HotwireActivityObserver import dev.hotwire.core.turbo.visit.VisitOptions @@ -46,16 +46,16 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } /** - * Gets the Activity's currently active [NavigatorHost]. + * Get the Activity's currently active [Navigator]. */ - val currentNavigatorHost: NavigatorHost - get() = navigatorHost(currentNavigatorHostId) - - /** - * Gets the currently active destination hosted in the current [NavigatorHost]. - */ - val currentNavDestination: HotwireNavDestination? - get() = currentFragment as HotwireNavDestination? + val currentNavigator: Navigator? + get() { + return if (currentNavigatorHost.isAdded && !currentNavigatorHost.isDetached) { + currentNavigatorHost.navigator + } else { + null + } + } /** * Sets the currently active session in your Activity. If you use multiple @@ -116,7 +116,7 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { options: VisitOptions = VisitOptions(), bundle: Bundle? = null ) { - currentNavDestination?.navigate(location, options, bundle) + currentNavigator?.navigate(location, options, bundle) } /** @@ -124,7 +124,7 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { * more details. */ fun navigateUp() { - currentNavDestination?.navigateUp() + currentNavigator?.navigateUp() } /** @@ -132,14 +132,14 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { * more details. */ fun navigateBack() { - currentNavDestination?.navigateBack() + currentNavigator?.navigateBack() } /** * Clears the navigation back stack to the start destination. */ fun clearBackStack(onCleared: () -> Unit = {}) { - currentNavDestination?.clearBackStack(onCleared) + currentNavigator?.clearBackStack(onCleared) } /** @@ -147,7 +147,7 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { * more details. */ fun refresh(displayProgress: Boolean = true) { - currentNavDestination?.refresh(displayProgress) + currentNavigator?.currentDestination?.refresh(displayProgress) } private fun listenToDestinationChanges(navController: NavController) { @@ -157,17 +157,11 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } private fun updateOnBackPressedCallback(navController: NavController) { - if (navController == currentNavigatorHost.navController) { + if (navController == currentNavigatorHost.navController) { onBackPressedCallback.isEnabled = navController.previousBackStackEntry != null } } - private val currentFragment: Fragment? - get() { - return if (currentNavigatorHost.isAdded && !currentNavigatorHost.isDetached) { - currentNavigatorHost.childFragmentManager.primaryNavigationFragment - } else { - null - } - } + private val currentNavigatorHost: NavigatorHost + get() = navigatorHost(currentNavigatorHostId) } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt index 5e31ab9..7ad9a22 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt @@ -103,7 +103,7 @@ class TurboFragmentDelegate(private val navDestination: HotwireNavDestination) { } it.setNavigationOnClickListener { - navDestination.navigateUp() + navigator.navigateUp() } } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt index d48e44f..7e26606 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt @@ -1,12 +1,9 @@ package dev.hotwire.core.turbo.delegates -import android.os.Bundle import androidx.annotation.IdRes import androidx.fragment.app.Fragment -import androidx.navigation.NavController import dev.hotwire.core.navigation.session.NavigatorHost -import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.visit.VisitOptions +import dev.hotwire.core.turbo.nav.Navigator /** * A simplified delegate that can be used when a [NavigatorHost] is nested @@ -24,64 +21,8 @@ import dev.hotwire.core.turbo.visit.VisitOptions class TurboNestedFragmentDelegate(val fragment: Fragment, navigatorHostId: Int) { val navigatorHost by lazy { findNavigatorHost(navigatorHostId) } - val currentNavDestination: HotwireNavDestination - get() = currentFragment as HotwireNavDestination - - /** - * Resets the navigator via [NavigatorHost.navigator.reset] - */ - fun resetNavigator() { - navigatorHost.navigator.reset() - } - - /** - * Resets the Turbo session associated with the nav host fragment. - */ - fun resetSession() { - navigatorHost.navigator.session.reset() - } - - /** - * Navigates to the specified location. The resulting destination and its presentation - * will be determined using the path configuration rules. - * - * @param location The location to navigate to. - * @param options Visit options to apply to the visit. (optional) - * @param bundle Bundled arguments to pass to the destination. (optional) - */ - fun navigate( - location: String, - options: VisitOptions = VisitOptions(), - bundle: Bundle? = null - ) { - currentNavDestination.navigate(location, options, bundle) - } - - /** - * Navigates up to the previous destination. See [NavController.navigateUp] for - * more details. - */ - fun navigateUp() { - currentNavDestination.navigateUp() - } - - /** - * Navigates back to the previous destination. See [NavController.popBackStack] for - * more details. - */ - fun navigateBack() { - currentNavDestination.navigateBack() - } - - /** - * Clears the navigation back stack to the start destination. - */ - fun clearBackStack() { - currentNavDestination.clearBackStack() - } - - private val currentFragment: Fragment - get() = navigatorHost.childFragmentManager.primaryNavigationFragment as Fragment + val navigator: Navigator + get() = navigatorHost.navigator private fun findNavigatorHost(@IdRes navHostFragmentId: Int): NavigatorHost { return fragment.childFragmentManager.findFragmentById(navHostFragmentId) as? NavigatorHost diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt index caf5bad..83fb6b5 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt @@ -50,12 +50,6 @@ interface HotwireNavDestination { val location: String get() = requireNotNull(fragment.arguments?.location) - /** - * Gets the previous back stack entry's location from the nav controller. - */ - val previousLocation: String? - get() = navController()?.previousBackStackEntry?.arguments?.location - /** * Gets the path configuration properties for the location associated with this * destination. @@ -140,24 +134,6 @@ interface HotwireNavDestination { ) } - /** - * Navigates to the specified location. The resulting destination and its presentation - * will be determined using the path configuration rules. - * - * @param location The location to navigate to. - * @param options Visit options to apply to the visit. (optional) - * @param bundle Bundled arguments to pass to the destination. (optional) - * @param extras Extras that can be passed to enable Fragment specific behavior. (optional) - */ - fun navigate( - location: String, - options: VisitOptions = VisitOptions(), - bundle: Bundle? = null, - extras: FragmentNavigator.Extras? = null - ) { - navigator.navigate(location, options, bundle, extras) - } - /** * Gets the default set of navigation options (basic enter/exit animations) for the Android * Navigation component to use to execute a navigation event. This can be overridden if @@ -192,29 +168,6 @@ interface HotwireNavDestination { } } - /** - * Navigates up to the previous destination. See [NavController.navigateUp] for - * more details. - */ - fun navigateUp() { - navigator.navigateUp() - } - - /** - * Navigates back to the previous destination. See [NavController.popBackStack] for - * more details. - */ - fun navigateBack() { - navigator.navigateBack() - } - - /** - * Clears the navigation back stack to the start destination. - */ - fun clearBackStack(onCleared: () -> Unit = {}) { - navigator.clearBackStack(onCleared) - } - /** * Gets a registered activity result launcher instance for the given `requestCode`. * @@ -229,30 +182,8 @@ interface HotwireNavDestination { return null } - /** - * Finds the nav host fragment with the given resource ID. - */ - fun findNavHostFragment(@IdRes navHostFragmentId: Int): NavigatorHost { - return fragment.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) - ?: fragment.parentFragment?.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) - ?: fragment.requireActivity().supportFragmentManager.findNavHostFragment(navHostFragmentId) - ?: throw IllegalStateException("No NavigatorHost found with ID: $navHostFragmentId") - } - fun prepareNavigation(onReady: () -> Unit) private val Bundle.location get() = getString("location") - - /** - * Retrieve the nav controller indirectly from the parent NavHostFragment, - * since it's only available when the fragment is attached to its parent. - */ - private fun navController(): NavController? { - return fragment.parentFragment?.findNavController() - } - - private fun FragmentManager.findNavHostFragment(navHostFragmentId: Int): NavigatorHost? { - return findFragmentById(navHostFragmentId) as? NavigatorHost - } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt index dd17551..6cc34b8 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt @@ -1,6 +1,8 @@ package dev.hotwire.core.turbo.nav import android.os.Bundle +import androidx.annotation.IdRes +import androidx.fragment.app.FragmentManager import androidx.navigation.NavBackStackEntry import androidx.navigation.NavController import androidx.navigation.NavOptions @@ -26,6 +28,18 @@ class Navigator(val host: NavigatorHost) { get() = host.childFragmentManager.primaryNavigationFragment as HotwireNavDestination? ?: throw IllegalStateException("No current destination found in NavHostFragment") + /** + * Gets the location for the current destination. + */ + val location: String? + get() = navController.currentBackStackEntry?.location + + /** + * Gets the location for the previous destination on the backstack. + */ + val previousLocation: String? + get() = navController.previousBackStackEntry?.location + /** * The [Session] instance that is shared with all destinations that are * hosted inside this [NavigatorHost]. @@ -48,6 +62,9 @@ class Navigator(val host: NavigatorHost) { return navController.previousBackStackEntry == null } + /** + * Navigates up to the previous destination. + */ fun navigateUp() { navigateWhenReady { val currentFragment = currentDestination.fragment @@ -59,6 +76,10 @@ class Navigator(val host: NavigatorHost) { } } + /** + * Navigates back to the previous destination. See [NavController.popBackStack] for + * more details. + */ fun navigateBack() { navigateWhenReady { val currentFragment = currentDestination.fragment @@ -70,9 +91,18 @@ class Navigator(val host: NavigatorHost) { } } + /** + * Navigates to the specified location. The resulting destination and its presentation + * will be determined using the path configuration rules. + * + * @param location The location to navigate to. + * @param options Visit options to apply to the visit. (optional) + * @param bundle Bundled arguments to pass to the destination. (optional) + * @param extras Extras that can be passed to enable Fragment specific behavior. (optional) + */ fun navigate( location: String, - options: VisitOptions, + options: VisitOptions = VisitOptions(), bundle: Bundle? = null, extras: FragmentNavigator.Extras? = null ) { @@ -118,6 +148,9 @@ class Navigator(val host: NavigatorHost) { } } + /** + * Clears the navigation back stack to the start destination. + */ fun clearBackStack(onCleared: () -> Unit = {}) { if (isAtStartDestination()) { onCleared() @@ -154,6 +187,18 @@ class Navigator(val host: NavigatorHost) { } } + /** + * Finds the [NavigatorHost] with the given resource ID. + */ + fun findNavHostFragment(@IdRes navHostFragmentId: Int): NavigatorHost { + val fragment = currentDestination.fragment + + return fragment.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) + ?: fragment.parentFragment?.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) + ?: fragment.requireActivity().supportFragmentManager.findNavHostFragment(navHostFragmentId) + ?: throw IllegalStateException("No NavigatorHost found with ID: $navHostFragmentId") + } + private fun navigateWhenReady(onReady: () -> Unit) { currentDestination.onBeforeNavigation() currentDestination.prepareNavigation(onReady) @@ -338,6 +383,10 @@ class Navigator(val host: NavigatorHost) { ) } + private fun FragmentManager.findNavHostFragment(navHostFragmentId: Int): NavigatorHost? { + return findFragmentById(navHostFragmentId) as? NavigatorHost + } + private val NavBackStackEntry?.isModalContext: Boolean get() { val context = this?.arguments?.getSerializable("presentation-context") diff --git a/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt b/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt index 39906bd..e4dc8ec 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt @@ -34,6 +34,6 @@ class NumbersFragment : HotwireFragment(), NumbersFragmentCallback { } override fun onItemClicked(number: Int) { - navigate("${Urls.numbersUrl}/$number") + navigator.navigate("${Urls.numbersUrl}/$number") } } diff --git a/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt b/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt index 4bf6e6c..0ccc283 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt @@ -29,7 +29,7 @@ open class WebFragment : HotwireWebFragment() { override fun onVisitErrorReceived(location: String, error: VisitError) { if (error is HttpError.ClientError.Unauthorized) { - navigate(Urls.signInUrl, VisitOptions(action = REPLACE)) + navigator.navigate(Urls.signInUrl, VisitOptions(action = REPLACE)) } else { super.onVisitErrorReceived(location, error) } From 9f5acac134e7d1f2e78de859ef9ab5ad7b1b8eea Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Mon, 20 May 2024 15:30:47 -0400 Subject: [PATCH 04/14] Rename the main public navigator methods to navigate so they're consistent with iOS --- .../activities/HotwireActivityDelegate.kt | 49 +------------------ .../turbo/delegates/TurboFragmentDelegate.kt | 5 +- .../delegates/TurboWebFragmentDelegate.kt | 4 +- .../dev/hotwire/core/turbo/nav/Navigator.kt | 25 +++++----- .../demo/features/numbers/NumbersFragment.kt | 2 +- .../hotwire/demo/features/web/WebFragment.kt | 2 +- 6 files changed, 19 insertions(+), 68 deletions(-) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt index 5ffa4c7..12a774c 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt @@ -23,7 +23,7 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { private val onBackPressedCallback = object : OnBackPressedCallback(enabled = true) { override fun handleOnBackPressed() { - navigateBack() + currentNavigator?.pop() } } @@ -103,53 +103,6 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { navigatorHosts.forEach { it.value.navigator.reset() } } - /** - * Navigates to the specified location. The resulting destination and its presentation - * will be determined using the path configuration rules. - * - * @param location The location to navigate to. - * @param options Visit options to apply to the visit. (optional) - * @param bundle Bundled arguments to pass to the destination. (optional) - */ - fun navigate( - location: String, - options: VisitOptions = VisitOptions(), - bundle: Bundle? = null - ) { - currentNavigator?.navigate(location, options, bundle) - } - - /** - * Navigates up to the previous destination. See [NavController.navigateUp] for - * more details. - */ - fun navigateUp() { - currentNavigator?.navigateUp() - } - - /** - * Navigates back to the previous destination. See [NavController.popBackStack] for - * more details. - */ - fun navigateBack() { - currentNavigator?.navigateBack() - } - - /** - * Clears the navigation back stack to the start destination. - */ - fun clearBackStack(onCleared: () -> Unit = {}) { - currentNavigator?.clearBackStack(onCleared) - } - - /** - * Refresh the current destination. See [HotwireNavDestination.refresh] for - * more details. - */ - fun refresh(displayProgress: Boolean = true) { - currentNavigator?.currentDestination?.refresh(displayProgress) - } - private fun listenToDestinationChanges(navController: NavController) { navController.addOnDestinationChangedListener { controller, _, _ -> updateOnBackPressedCallback(controller) diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt index 7ad9a22..05ec5b8 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt @@ -3,7 +3,6 @@ package dev.hotwire.core.turbo.delegates import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.turbo.fragments.TurboFragmentViewModel import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.session.SessionModalResult import dev.hotwire.core.turbo.session.SessionViewModel import dev.hotwire.core.turbo.util.displayBackButton @@ -66,7 +65,7 @@ class TurboFragmentDelegate(private val navDestination: HotwireNavDestination) { fun onStartAfterModalResult(result: SessionModalResult) { logEvent("fragment.onStartAfterModalResult", "location" to result.location, "options" to result.options) if (result.shouldNavigate) { - navigator.navigate(result.location, result.options, result.bundle) + navigator.route(result.location, result.options, result.bundle) } } @@ -103,7 +102,7 @@ class TurboFragmentDelegate(private val navDestination: HotwireNavDestination) { } it.setNavigationOnClickListener { - navigator.navigateUp() + navigator.popUp() } } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt index cb68266..d642213 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboWebFragmentDelegate.kt @@ -199,7 +199,7 @@ internal class TurboWebFragmentDelegate( } override fun onRenderProcessGone() { - navigator.navigate(location, VisitOptions(action = VisitAction.REPLACE)) + navigator.route(location, VisitOptions(action = VisitAction.REPLACE)) } override fun requestFailedWithError(visitHasCachedSnapshot: Boolean, error: VisitError) { @@ -218,7 +218,7 @@ internal class TurboWebFragmentDelegate( location: String, options: VisitOptions ) { - navigator.navigate(location, options) + navigator.route(location, options) } override fun visitNavDestination(): HotwireNavDestination { diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt index 6cc34b8..5334ea6 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt @@ -63,9 +63,9 @@ class Navigator(val host: NavigatorHost) { } /** - * Navigates up to the previous destination. + * Pops the backstack up to the previous destination. */ - fun navigateUp() { + fun popUp() { navigateWhenReady { val currentFragment = currentDestination.fragment if (currentFragment is HotwireNavDialogDestination) { @@ -77,10 +77,9 @@ class Navigator(val host: NavigatorHost) { } /** - * Navigates back to the previous destination. See [NavController.popBackStack] for - * more details. + * Pops the backstack to the previous destination. */ - fun navigateBack() { + fun pop() { navigateWhenReady { val currentFragment = currentDestination.fragment if (currentFragment is HotwireNavDialogDestination) { @@ -92,7 +91,7 @@ class Navigator(val host: NavigatorHost) { } /** - * Navigates to the specified location. The resulting destination and its presentation + * Routes to the specified location. The resulting destination and its presentation * will be determined using the path configuration rules. * * @param location The location to navigate to. @@ -100,7 +99,7 @@ class Navigator(val host: NavigatorHost) { * @param bundle Bundled arguments to pass to the destination. (optional) * @param extras Extras that can be passed to enable Fragment specific behavior. (optional) */ - fun navigate( + fun route( location: String, options: VisitOptions = VisitOptions(), bundle: Bundle? = null, @@ -140,7 +139,7 @@ class Navigator(val host: NavigatorHost) { navigateWithinContext(rule) } TurboNavMode.REFRESH -> { - navigate(rule.currentLocation, VisitOptions()) + route(rule.currentLocation, VisitOptions()) } TurboNavMode.NONE -> { // Do nothing @@ -149,9 +148,9 @@ class Navigator(val host: NavigatorHost) { } /** - * Clears the navigation back stack to the start destination. + * Clears the navigation backstack to the start destination. */ - fun clearBackStack(onCleared: () -> Unit = {}) { + fun clearAll(onCleared: () -> Unit = {}) { if (isAtStartDestination()) { onCleared() return @@ -169,12 +168,12 @@ class Navigator(val host: NavigatorHost) { } /** - * Resets the [Navigator] along with its [NavigatorHost] and [Session] instances. + * Resets the [Navigator] along with its [NavigatorHost] and [Session] instance. * The entire navigation graph is reset to its original starting point. */ fun reset(onReset: () -> Unit = {}) { navigateWhenReady { - clearBackStack { + clearAll { session.reset() host.initControllerGraph() @@ -226,7 +225,7 @@ class Navigator(val host: NavigatorHost) { replaceRootLocation(rule) } TurboNavPresentation.CLEAR_ALL -> navigateWhenReady { - clearBackStack() + clearAll() } else -> { throw IllegalStateException("Unexpected Presentation for navigating within context") diff --git a/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt b/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt index e4dc8ec..f4034f2 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/features/numbers/NumbersFragment.kt @@ -34,6 +34,6 @@ class NumbersFragment : HotwireFragment(), NumbersFragmentCallback { } override fun onItemClicked(number: Int) { - navigator.navigate("${Urls.numbersUrl}/$number") + navigator.route("${Urls.numbersUrl}/$number") } } diff --git a/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt b/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt index 0ccc283..a920ce4 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/features/web/WebFragment.kt @@ -29,7 +29,7 @@ open class WebFragment : HotwireWebFragment() { override fun onVisitErrorReceived(location: String, error: VisitError) { if (error is HttpError.ClientError.Unauthorized) { - navigator.navigate(Urls.signInUrl, VisitOptions(action = REPLACE)) + navigator.route(Urls.signInUrl, VisitOptions(action = REPLACE)) } else { super.onVisitErrorReceived(location, error) } From 5a50a074fd50757339cfd67ffbb4bd1ce17586ed Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Mon, 20 May 2024 16:17:27 -0400 Subject: [PATCH 05/14] Replace navHostForNavigation() with navigatorForNavigation() for nested fragement navigation --- .../core/turbo/nav/HotwireNavDestination.kt | 14 +++----------- .../kotlin/dev/hotwire/core/turbo/nav/Navigator.kt | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt index 83fb6b5..6c16840 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt @@ -3,21 +3,15 @@ package dev.hotwire.core.turbo.nav import android.content.Intent import android.os.Bundle import androidx.activity.result.ActivityResultLauncher -import androidx.annotation.IdRes import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.navigation.NavController import androidx.navigation.NavOptions -import androidx.navigation.fragment.FragmentNavigator -import androidx.navigation.fragment.findNavController import androidx.navigation.navOptions import dev.hotwire.core.R import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.navigation.activities.HotwireActivity import dev.hotwire.core.navigation.routing.Router -import dev.hotwire.core.navigation.session.NavigatorHost import dev.hotwire.core.turbo.config.PathConfigurationProperties import dev.hotwire.core.turbo.config.context import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate @@ -26,7 +20,6 @@ import dev.hotwire.core.turbo.fragments.TurboFragment import dev.hotwire.core.turbo.fragments.TurboFragmentViewModel import dev.hotwire.core.turbo.fragments.TurboWebFragment import dev.hotwire.core.turbo.visit.VisitAction -import dev.hotwire.core.turbo.visit.VisitOptions /** * The primary interface that a navigable Fragment implements to provide the library with @@ -111,13 +104,12 @@ interface HotwireNavDestination { fun refresh(displayProgress: Boolean = true) /** - * Gets the nav host fragment that will be used for navigating to `newLocation`. You should + * Gets the navigator that will be used for navigating to `newLocation`. You should * not have to override this, unless you're using a [TurboNestedFragmentDelegate] to provide * sub-navigation within your current Fragment destination and would like custom behavior. */ - // TODO replace with `navigatorForNavigation()` - fun navHostForNavigation(newLocation: String): NavigatorHost { - return navigator.host + fun navigatorForNavigation(newLocation: String): Navigator { + return navigator } /** diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt index 5334ea6..9804335 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt @@ -358,7 +358,7 @@ class Navigator(val host: NavigatorHost) { } private fun currentControllerForLocation(location: String): NavController { - return currentDestination.navHostForNavigation(location).navController + return currentDestination.navigatorForNavigation(location).navController } private fun getRouteResult(location: String): Router.RouteResult { From fcccfae2ab63525d68351575d55fb0f00f428db7 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Mon, 20 May 2024 16:21:07 -0400 Subject: [PATCH 06/14] Fix tests --- .../kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt b/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt index 6abe1a1..7dae85c 100644 --- a/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt @@ -9,6 +9,7 @@ import com.nhaarman.mockito_kotlin.never import com.nhaarman.mockito_kotlin.whenever import dev.hotwire.core.config.Hotwire import dev.hotwire.core.turbo.nav.HotwireNavDestination +import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.session.Session import org.junit.Assert.* import org.junit.Before @@ -21,6 +22,7 @@ class BridgeDelegateTest { private lateinit var lifecycleOwner: TestLifecycleOwner private val bridge: Bridge = mock() private val webView: WebView = mock() + private val navigator: Navigator = mock() private val destination: HotwireNavDestination = mock() private val session: Session = mock() @@ -31,7 +33,8 @@ class BridgeDelegateTest { @Before fun setup() { whenever(bridge.webView).thenReturn(webView) - whenever(destination.session).thenReturn(session) + whenever(destination.navigator).thenReturn(navigator) + whenever(destination.navigator.session).thenReturn(session) whenever(session.isReady).thenReturn(true) Hotwire.registerBridgeComponents(TestData.componentFactories) From d02cb28ce179105d9c28a9117083055a484a793a Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 07:54:51 -0400 Subject: [PATCH 07/14] Rename SessionConfiguration -> NavigatorConfiguration --- .../navigation/activities/HotwireActivity.kt | 4 +-- .../activities/HotwireActivityDelegate.kt | 15 ++++------ .../navigation/routing/AppNavigationRoute.kt | 8 +++--- .../core/navigation/routing/BrowserRoute.kt | 8 +++--- .../navigation/routing/BrowserTabRoute.kt | 8 +++--- .../hotwire/core/navigation/routing/Router.kt | 12 ++++---- ...iguration.kt => NavigatorConfiguration.kt} | 4 +-- .../core/navigation/session/NavigatorHost.kt | 6 ++-- .../delegates/TurboNestedFragmentDelegate.kt | 6 ++-- .../core/turbo/nav/HotwireNavDestination.kt | 2 +- .../dev/hotwire/core/turbo/nav/Navigator.kt | 18 ++++++------ .../dev/hotwire/core/turbo/session/Session.kt | 4 +-- .../routing/AppNavigationRouteTest.kt | 12 ++++---- .../navigation/routing/BrowserRouteTest.kt | 12 ++++---- .../navigation/routing/BrowserTabRouteTest.kt | 12 ++++---- .../dev/hotwire/demo/main/MainActivity.kt | 8 +++--- docs/QUICK-START.md | 28 +++++++++---------- 17 files changed, 82 insertions(+), 85 deletions(-) rename core/src/main/kotlin/dev/hotwire/core/navigation/session/{SessionConfiguration.kt => NavigatorConfiguration.kt} (65%) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt index 0b0ae97..c06f8d8 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt @@ -2,7 +2,7 @@ package dev.hotwire.core.navigation.activities import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration /** * Activity that should be implemented by any Activity using Hotwire. @@ -11,7 +11,7 @@ abstract class HotwireActivity : AppCompatActivity() { lateinit var delegate: HotwireActivityDelegate private set - abstract fun sessionConfigurations(): List + abstract fun navigatorConfigurations(): List override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt index 12a774c..8ea3014 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt @@ -1,15 +1,12 @@ package dev.hotwire.core.navigation.activities -import android.os.Bundle import androidx.activity.OnBackPressedCallback import androidx.annotation.IdRes import androidx.navigation.NavController import dev.hotwire.core.navigation.session.NavigatorHost -import dev.hotwire.core.navigation.session.SessionConfiguration -import dev.hotwire.core.turbo.nav.HotwireNavDestination +import dev.hotwire.core.navigation.session.NavigatorConfiguration import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.observers.HotwireActivityObserver -import dev.hotwire.core.turbo.visit.VisitOptions /** * Initializes the Activity for Hotwire navigation and provides all the hooks for an @@ -27,7 +24,7 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } } - private var currentNavigatorHostId = activity.sessionConfigurations().first().navHostFragmentId + private var currentNavigatorHostId = activity.navigatorConfigurations().first().navigatorHostId set(value) { field = value updateOnBackPressedCallback(currentNavigatorHost.navController) @@ -58,12 +55,12 @@ class HotwireActivityDelegate(val activity: HotwireActivity) { } /** - * Sets the currently active session in your Activity. If you use multiple + * Sets the currently active navigator in your Activity. If you use multiple * [NavigatorHost] instances in your app (such as for bottom tabs), - * you must update this whenever the current session changes. + * you must update this whenever the current navigator changes. */ - fun setCurrentSession(sessionConfiguration: SessionConfiguration) { - currentNavigatorHostId = sessionConfiguration.navHostFragmentId + fun setCurrentNavigator(configuration: NavigatorConfiguration) { + currentNavigatorHostId = configuration.navigatorHostId } internal fun registerNavigatorHost(host: NavigatorHost) { diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt index 8623d7f..ee39b2b 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt @@ -2,7 +2,7 @@ package dev.hotwire.core.navigation.routing import androidx.core.net.toUri import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration class AppNavigationRoute : Router.Route { override val name = "app-navigation" @@ -11,14 +11,14 @@ class AppNavigationRoute : Router.Route { override fun matches( location: String, - sessionConfiguration: SessionConfiguration + configuration: NavigatorConfiguration ): Boolean { - return sessionConfiguration.startLocation.toUri().host == location.toUri().host + return configuration.startLocation.toUri().host == location.toUri().host } override fun handle( location: String, - sessionConfiguration: SessionConfiguration, + configuration: NavigatorConfiguration, activity: HotwireActivity ) { // No-op diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt index 29f8e25..1154b4d 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt @@ -5,7 +5,7 @@ import android.content.Intent import androidx.core.net.toUri import dev.hotwire.core.lib.logging.logError import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration class BrowserRoute : Router.Route { override val name = "browser" @@ -14,14 +14,14 @@ class BrowserRoute : Router.Route { override fun matches( location: String, - sessionConfiguration: SessionConfiguration + configuration: NavigatorConfiguration ): Boolean { - return sessionConfiguration.startLocation.toUri().host != location.toUri().host + return configuration.startLocation.toUri().host != location.toUri().host } override fun handle( location: String, - sessionConfiguration: SessionConfiguration, + configuration: NavigatorConfiguration, activity: HotwireActivity ) { val intent = Intent(Intent.ACTION_VIEW, location.toUri()) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt index b07f625..8d94fce 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt @@ -5,7 +5,7 @@ import androidx.browser.customtabs.CustomTabsIntent import androidx.core.net.toUri import com.google.android.material.R import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration import dev.hotwire.core.turbo.util.colorFromThemeAttr class BrowserTabRoute : Router.Route { @@ -15,14 +15,14 @@ class BrowserTabRoute : Router.Route { override fun matches( location: String, - sessionConfiguration: SessionConfiguration + configuration: NavigatorConfiguration ): Boolean { - return sessionConfiguration.startLocation.toUri().host != location.toUri().host + return configuration.startLocation.toUri().host != location.toUri().host } override fun handle( location: String, - sessionConfiguration: SessionConfiguration, + configuration: NavigatorConfiguration, activity: HotwireActivity ) { val color = activity.colorFromThemeAttr(R.attr.colorSurface) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt index d5cda93..f0223b0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt @@ -2,7 +2,7 @@ package dev.hotwire.core.navigation.routing import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration import dev.hotwire.core.navigation.routing.Router.Route /** @@ -34,7 +34,7 @@ class Router(private val routes: List) { */ fun matches( location: String, - sessionConfiguration: SessionConfiguration + configuration: NavigatorConfiguration ): Boolean /** @@ -43,7 +43,7 @@ class Router(private val routes: List) { */ fun handle( location: String, - sessionConfiguration: SessionConfiguration, + configuration: NavigatorConfiguration, activity: HotwireActivity ) } @@ -62,17 +62,17 @@ class Router(private val routes: List) { internal fun route( location: String, - sessionConfiguration: SessionConfiguration, + configuration: NavigatorConfiguration, activity: HotwireActivity ): RouteResult { routes.forEach { route -> - if (route.matches(location, sessionConfiguration)) { + if (route.matches(location, configuration)) { logEvent("routeMatch", listOf( "route" to route.name, "location" to location )) - route.handle(location, sessionConfiguration, activity) + route.handle(location, configuration, activity) return route.result } } diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionConfiguration.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorConfiguration.kt similarity index 65% rename from core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionConfiguration.kt rename to core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorConfiguration.kt index dea5e27..0f38273 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/session/SessionConfiguration.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorConfiguration.kt @@ -2,8 +2,8 @@ package dev.hotwire.core.navigation.session import androidx.annotation.IdRes -data class SessionConfiguration( +data class NavigatorConfiguration( val name: String, val startLocation: String, - @IdRes val navHostFragmentId: Int, + @IdRes val navigatorHostId: Int, ) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt index 4e15caa..ce408e0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt @@ -14,8 +14,8 @@ open class NavigatorHost : NavHostFragment() { lateinit var navigator: Navigator private set - val sessionConfiguration get() = activity.sessionConfigurations().first { - id == it.navHostFragmentId + val configuration get() = activity.navigatorConfigurations().first { + id == it.navigatorHostId } override fun onCreate(savedInstanceState: Bundle?) { @@ -36,7 +36,7 @@ open class NavigatorHost : NavHostFragment() { internal fun initControllerGraph() { navController.apply { graph = TurboNavGraphBuilder( - startLocation = sessionConfiguration.startLocation, + startLocation = configuration.startLocation, pathConfiguration = pathConfiguration, navController = findNavController() ).build( diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt index 7e26606..48bd1db 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt @@ -24,8 +24,8 @@ class TurboNestedFragmentDelegate(val fragment: Fragment, navigatorHostId: Int) val navigator: Navigator get() = navigatorHost.navigator - private fun findNavigatorHost(@IdRes navHostFragmentId: Int): NavigatorHost { - return fragment.childFragmentManager.findFragmentById(navHostFragmentId) as? NavigatorHost - ?: throw IllegalStateException("No NavigatorHost found with ID: $navHostFragmentId") + private fun findNavigatorHost(@IdRes navigatorHostId: Int): NavigatorHost { + return fragment.childFragmentManager.findFragmentById(navigatorHostId) as? NavigatorHost + ?: throw IllegalStateException("No NavigatorHost found with ID: $navigatorHostId") } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt index 6c16840..436e0f6 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt @@ -121,7 +121,7 @@ interface HotwireNavDestination { fun route(newLocation: String): Router.RouteResult { return Hotwire.router.route( location = newLocation, - sessionConfiguration = navigator.host.sessionConfiguration, + configuration = navigator.host.configuration, activity = fragment.requireActivity() as HotwireActivity ) } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt index 9804335..3d57e1b 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt @@ -26,7 +26,7 @@ class Navigator(val host: NavigatorHost) { */ val currentDestination: HotwireNavDestination get() = host.childFragmentManager.primaryNavigationFragment as HotwireNavDestination? - ?: throw IllegalStateException("No current destination found in NavHostFragment") + ?: throw IllegalStateException("No current destination found in NavigatorHost") /** * Gets the location for the current destination. @@ -48,7 +48,7 @@ class Navigator(val host: NavigatorHost) { private set internal fun createNewSession() = Session( - sessionName = host.sessionConfiguration.name, + sessionName = host.configuration.name, activity = host.activity, webView = Hotwire.config.makeCustomWebView(host.requireContext()) ).also { @@ -189,13 +189,13 @@ class Navigator(val host: NavigatorHost) { /** * Finds the [NavigatorHost] with the given resource ID. */ - fun findNavHostFragment(@IdRes navHostFragmentId: Int): NavigatorHost { + fun findNavigatorHost(@IdRes navigatorHostId: Int): NavigatorHost { val fragment = currentDestination.fragment - return fragment.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) - ?: fragment.parentFragment?.parentFragment?.childFragmentManager?.findNavHostFragment(navHostFragmentId) - ?: fragment.requireActivity().supportFragmentManager.findNavHostFragment(navHostFragmentId) - ?: throw IllegalStateException("No NavigatorHost found with ID: $navHostFragmentId") + return fragment.parentFragment?.childFragmentManager?.findNavigatorHost(navigatorHostId) + ?: fragment.parentFragment?.parentFragment?.childFragmentManager?.findNavigatorHost(navigatorHostId) + ?: fragment.requireActivity().supportFragmentManager.findNavigatorHost(navigatorHostId) + ?: throw IllegalStateException("No NavigatorHost found with ID: $navigatorHostId") } private fun navigateWhenReady(onReady: () -> Unit) { @@ -382,8 +382,8 @@ class Navigator(val host: NavigatorHost) { ) } - private fun FragmentManager.findNavHostFragment(navHostFragmentId: Int): NavigatorHost? { - return findFragmentById(navHostFragmentId) as? NavigatorHost + private fun FragmentManager.findNavigatorHost(navigatorHostId: Int): NavigatorHost? { + return findFragmentById(navigatorHostId) as? NavigatorHost } private val NavBackStackEntry?.isModalContext: Boolean diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/session/Session.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/session/Session.kt index 78f2c14..4d85c38 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/session/Session.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/session/Session.kt @@ -32,8 +32,8 @@ import java.util.* /** * This class is primarily responsible for managing an instance of an Android WebView that will - * be shared between fragments. An implementation of [SessionNavHostFragment] will create - * a session for you and it can be retrieved via [SessionNavHostFragment.session]. + * be shared between destinations. A a [Navigator] will create a session for you and it can be + * retrieved via [Navigator.session]. * * @property sessionName An arbitrary name to be used as an identifier for a given session. * @property activity The activity to which the session will be bound to. diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt index edad3f1..3b72fdc 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt @@ -1,6 +1,6 @@ package dev.hotwire.core.navigation.routing -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith @@ -9,10 +9,10 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class AppNavigationRouteTest { private val route = AppNavigationRoute() - private val sessionConfig = SessionConfiguration( + private val config = NavigatorConfiguration( name = "test", startLocation = "https://my.app.com", - navHostFragmentId = 0 + navigatorHostId = 0 ) @Test @@ -23,18 +23,18 @@ class AppNavigationRouteTest { @Test fun `url on app domain matches`() { val url = "https://my.app.com/page" - assertTrue(route.matches(url, sessionConfig)) + assertTrue(route.matches(url, config)) } @Test fun `url without subdomain does not match`() { val url = "https://app.com/page" - assertFalse(route.matches(url, sessionConfig)) + assertFalse(route.matches(url, config)) } @Test fun `masqueraded url does not match`() { val url = "https://app.my.com@fake.domain" - assertFalse(route.matches(url, sessionConfig)) + assertFalse(route.matches(url, config)) } } diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt index 40222b9..5faf243 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt @@ -1,6 +1,6 @@ package dev.hotwire.core.navigation.routing -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith @@ -9,10 +9,10 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class BrowserRouteTest { private val route = BrowserRoute() - private val sessionConfig = SessionConfiguration( + private val config = NavigatorConfiguration( name = "test", startLocation = "https://my.app.com", - navHostFragmentId = 0 + navigatorHostId = 0 ) @Test @@ -23,18 +23,18 @@ class BrowserRouteTest { @Test fun `url on external domain matches`() { val url = "https://external.com/page" - assertTrue(route.matches(url, sessionConfig)) + assertTrue(route.matches(url, config)) } @Test fun `url without subdomain matches`() { val url = "https://app.com/page" - assertTrue(route.matches(url, sessionConfig)) + assertTrue(route.matches(url, config)) } @Test fun `url on app domain does not match`() { val url = "https://my.app.com/page" - assertFalse(route.matches(url, sessionConfig)) + assertFalse(route.matches(url, config)) } } diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt index 396a7b2..d4267ee 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt @@ -1,6 +1,6 @@ package dev.hotwire.core.navigation.routing -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith @@ -9,10 +9,10 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class BrowserTabRouteTest { private val route = BrowserTabRoute() - private val sessionConfig = SessionConfiguration( + private val config = NavigatorConfiguration( name = "test", startLocation = "https://my.app.com", - navHostFragmentId = 0 + navigatorHostId = 0 ) @Test @@ -23,18 +23,18 @@ class BrowserTabRouteTest { @Test fun `url on external domain matches`() { val url = "https://external.com/page" - assertTrue(route.matches(url, sessionConfig)) + assertTrue(route.matches(url, config)) } @Test fun `url without subdomain matches`() { val url = "https://app.com/page" - assertTrue(route.matches(url, sessionConfig)) + assertTrue(route.matches(url, config)) } @Test fun `url on app domain does not match`() { val url = "https://my.app.com/page" - assertFalse(route.matches(url, sessionConfig)) + assertFalse(route.matches(url, config)) } } diff --git a/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt b/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt index 0113e96..39f8577 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt @@ -2,7 +2,7 @@ package dev.hotwire.demo.main import android.os.Bundle import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.SessionConfiguration +import dev.hotwire.core.navigation.session.NavigatorConfiguration import dev.hotwire.demo.R import dev.hotwire.demo.Urls @@ -12,11 +12,11 @@ class MainActivity : HotwireActivity() { setContentView(R.layout.activity_main) } - override fun sessionConfigurations() = listOf( - SessionConfiguration( + override fun navigatorConfigurations() = listOf( + NavigatorConfiguration( name = "main", startLocation = Urls.homeUrl, - navHostFragmentId = R.id.main_nav_host + navigatorHostId = R.id.main_nav_host ) ) } diff --git a/docs/QUICK-START.md b/docs/QUICK-START.md index f335188..1d1cff0 100644 --- a/docs/QUICK-START.md +++ b/docs/QUICK-START.md @@ -2,7 +2,7 @@ ## Contents -1. [Introduction to NavHostFragments](#introduction-to-navhostfragments) +1. [Introduction to NavigatorHosts](#introduction-to-navhostfragments) 1. [Create an Activity](#create-an-activity) 1. [Configure your app](#configure-your-app) 1. [Create a Path Configuration](#create-a-path-configuration) @@ -10,11 +10,11 @@ 1. [Advanced Options](#advanced-options) 2. [Local Development](#local-development) -## Introduction to NavHostFragments +## Introduction to NavigatorHosts -A [`NavHostFragment`](https://developer.android.com/reference/androidx/navigation/fragment/NavHostFragment) is a component available in [Android Jetpack](https://developer.android.com/jetpack) and is primarily responsible for providing "an area in your layout for self-contained navigation to occur." +To start, a [`NavHostFragment`](https://developer.android.com/reference/androidx/navigation/fragment/NavHostFragment) is a component available in [Android Jetpack](https://developer.android.com/jetpack) and is primarily responsible for providing "an area in your layout for self-contained navigation to occur." -The Hotwire extension of this class, `SessionNavHostFragment`, along with being responsible for self-contained `HotwireFragment` navigation, also manages a `Sesssion` and a `TurboWebView` instance. You will need to place an instance of a `SessionNavHostFragment` in your main `Activity` layout. +The Hotwire implementation of this class, `NavigatorHost`, along with being responsible for self-contained `HotwireFragment` navigation, also manages a `Navigator` with a `Sesssion` and `TurboWebView` instance. ## Create an Activity @@ -22,7 +22,7 @@ It's strongly recommended to use a single-Activity architecture in your app. Gen ### Create the HotwireActivity layout resource -You need to create a layout resource file that your `HotwireActivity` will use to host the `SessionNavHostFragment`. +You need to create a layout resource file that your `HotwireActivity` will use to host the `NavigatorHost`. Android Jetpack provides a [`FragmentContainerView`](https://developer.android.com/reference/androidx/fragment/app/FragmentContainerView) to contain `NavHostFragment` navigation. In its simplest form, your Activity layout file will look like: @@ -37,7 +37,7 @@ Android Jetpack provides a [`FragmentContainerView`](https://developer.android.c @@ -49,14 +49,14 @@ Refer to the demo [`activity_main.xml`](../demo/src/main/res/layout/activity_mai ### Implement the HotwireActivity abstract class -A Hotwire `Activity` is straightforward and needs to subclass the [`HotwireActivity`](../core/src/main/kotlin/dev/hotwire/core/turbo/activities/HotwireActivity.kt) class in order to provide `Session` configuration information. +A Hotwire `Activity` is straightforward and needs to subclass the [`HotwireActivity`](../core/src/main/kotlin/dev/hotwire/core/turbo/activities/HotwireActivity.kt) class in order to provide `Navigator` configuration information. `HotwireActivity` extends Android Jetpack's [`AppCompatActivity`](https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity). -You'll need to provide at least one `SessionConfiguration` instance (one for each `SessionNavHostFragment` that exists in our Activity layout). This includes: -- The `name` of the `Session` (this is arbitrary and helpful for debugging purposes, but each must be unique in your app) +You'll need to provide at least one `NavigatorConfiguration` instance (one for each `NavigatorHost` that exists in our Activity layout). This includes: +- The `name` of the `Navigator` (this is arbitrary and helpful for debugging purposes, but each must be unique in your app) - The `startLocation` url when your app starts up. Note: if you're running your app locally without HTTPS, see the [local development](#local-development) section. -- The `navHostFragmentId`, which refers to the resource ID of the `SessionNavHostFragment` in your Activity layout. +- The `navigatorHostId`, which refers to the resource ID of the `NavigatorHost` in your Activity layout. In its simplest form, your Activity will look like: @@ -68,17 +68,17 @@ class MainActivity : HotwireActivity() { setContentView(R.layout.activity_main) } - override fun sessionConfigurations() = listOf( - SessionConfiguration( + override fun navigatorConfigurations() = listOf( + NavigatorConfiguration( name = "main", startLocation = Urls.homeUrl, - navHostFragmentId = R.id.main_nav_host + navigatorHostId = R.id.main_nav_host ) ) } ``` -_Note that `R.layout.activity_main` refers to the Activity layout file that you already created. `R.id.main_nav_host` refers to the `SessionNavHostFragment` that placed in the layout file._ +_Note that `R.layout.activity_main` refers to the Activity layout file that you already created. `R.id.main_nav_host` refers to the `NavigatorHost` that placed in the layout file._ Refer to the demo [`MainActivity`](../demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt) as an example. (Don't forget to add your Activity to your app's [`AndroidManifest.xml`](../demo/src/main/AndroidManifest.xml) file.) From bce8fbdf4c67e04e28fa0d42cbcacfce16a511b0 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 08:21:40 -0400 Subject: [PATCH 08/14] Rename the session package -> navigator --- .../navigation/activities/HotwireActivity.kt | 2 +- .../activities/HotwireActivityDelegate.kt | 6 ++-- .../nav => navigation/navigator}/Navigator.kt | 6 ++-- .../NavigatorConfiguration.kt | 2 +- .../{session => navigator}/NavigatorHost.kt | 3 +- .../navigation/routing/AppNavigationRoute.kt | 2 +- .../core/navigation/routing/BrowserRoute.kt | 2 +- .../navigation/routing/BrowserTabRoute.kt | 2 +- .../hotwire/core/navigation/routing/Router.kt | 2 +- .../delegates/TurboNestedFragmentDelegate.kt | 4 +-- .../TurboBottomSheetDialogFragment.kt | 4 +-- .../core/turbo/fragments/TurboFragment.kt | 4 +-- .../core/turbo/nav/HotwireNavDestination.kt | 1 + .../turbo/nav/HotwireNavDialogDestination.kt | 29 ------------------- .../hotwire/core/bridge/BridgeDelegateTest.kt | 2 +- .../routing/AppNavigationRouteTest.kt | 2 +- .../navigation/routing/BrowserRouteTest.kt | 2 +- .../navigation/routing/BrowserTabRouteTest.kt | 2 +- .../dev/hotwire/demo/main/MainActivity.kt | 2 +- demo/src/main/res/layout/activity_main.xml | 2 +- docs/QUICK-START.md | 2 +- 21 files changed, 28 insertions(+), 55 deletions(-) rename core/src/main/kotlin/dev/hotwire/core/{turbo/nav => navigation/navigator}/Navigator.kt (98%) rename core/src/main/kotlin/dev/hotwire/core/navigation/{session => navigator}/NavigatorConfiguration.kt (77%) rename core/src/main/kotlin/dev/hotwire/core/navigation/{session => navigator}/NavigatorHost.kt (94%) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt index c06f8d8..34dac08 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivity.kt @@ -2,7 +2,7 @@ package dev.hotwire.core.navigation.activities import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration /** * Activity that should be implemented by any Activity using Hotwire. diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt index 8ea3014..781bb14 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/activities/HotwireActivityDelegate.kt @@ -3,9 +3,9 @@ package dev.hotwire.core.navigation.activities import androidx.activity.OnBackPressedCallback import androidx.annotation.IdRes import androidx.navigation.NavController -import dev.hotwire.core.navigation.session.NavigatorHost -import dev.hotwire.core.navigation.session.NavigatorConfiguration -import dev.hotwire.core.turbo.nav.Navigator +import dev.hotwire.core.navigation.navigator.NavigatorHost +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.Navigator import dev.hotwire.core.turbo.observers.HotwireActivityObserver /** diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt similarity index 98% rename from core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt rename to core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt index 3d57e1b..60ef4a0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt @@ -1,4 +1,4 @@ -package dev.hotwire.core.turbo.nav +package dev.hotwire.core.navigation.navigator import android.os.Bundle import androidx.annotation.IdRes @@ -12,7 +12,9 @@ import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.navigation.routing.Router -import dev.hotwire.core.navigation.session.NavigatorHost +import dev.hotwire.core.turbo.nav.* +import dev.hotwire.core.turbo.nav.TurboNavMode +import dev.hotwire.core.turbo.nav.TurboNavRule import dev.hotwire.core.turbo.session.Session import dev.hotwire.core.turbo.util.location import dev.hotwire.core.turbo.visit.VisitAction diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorConfiguration.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorConfiguration.kt similarity index 77% rename from core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorConfiguration.kt rename to core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorConfiguration.kt index 0f38273..37d87a0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorConfiguration.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorConfiguration.kt @@ -1,4 +1,4 @@ -package dev.hotwire.core.navigation.session +package dev.hotwire.core.navigation.navigator import androidx.annotation.IdRes diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt similarity index 94% rename from core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt rename to core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt index ce408e0..ede0732 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/session/NavigatorHost.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt @@ -1,4 +1,4 @@ -package dev.hotwire.core.navigation.session +package dev.hotwire.core.navigation.navigator import android.os.Bundle import androidx.navigation.fragment.NavHostFragment @@ -6,7 +6,6 @@ import androidx.navigation.fragment.findNavController import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.turbo.nav.Navigator import dev.hotwire.core.turbo.nav.TurboNavGraphBuilder open class NavigatorHost : NavHostFragment() { diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt index ee39b2b..d3d5dd6 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRoute.kt @@ -2,7 +2,7 @@ package dev.hotwire.core.navigation.routing import androidx.core.net.toUri import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration class AppNavigationRoute : Router.Route { override val name = "app-navigation" diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt index 1154b4d..e5200de 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserRoute.kt @@ -5,7 +5,7 @@ import android.content.Intent import androidx.core.net.toUri import dev.hotwire.core.lib.logging.logError import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration class BrowserRoute : Router.Route { override val name = "browser" diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt index 8d94fce..a154d20 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRoute.kt @@ -5,7 +5,7 @@ import androidx.browser.customtabs.CustomTabsIntent import androidx.core.net.toUri import com.google.android.material.R import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration import dev.hotwire.core.turbo.util.colorFromThemeAttr class BrowserTabRoute : Router.Route { diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt index f0223b0..66fa7a0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/routing/Router.kt @@ -2,7 +2,7 @@ package dev.hotwire.core.navigation.routing import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration import dev.hotwire.core.navigation.routing.Router.Route /** diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt index 48bd1db..f3ff901 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboNestedFragmentDelegate.kt @@ -2,8 +2,8 @@ package dev.hotwire.core.turbo.delegates import androidx.annotation.IdRes import androidx.fragment.app.Fragment -import dev.hotwire.core.navigation.session.NavigatorHost -import dev.hotwire.core.turbo.nav.Navigator +import dev.hotwire.core.navigation.navigator.NavigatorHost +import dev.hotwire.core.navigation.navigator.Navigator /** * A simplified delegate that can be used when a [NavigatorHost] is nested diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt index ac08f78..918d448 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboBottomSheetDialogFragment.kt @@ -7,12 +7,12 @@ import android.view.View import androidx.appcompat.widget.Toolbar import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dev.hotwire.core.R -import dev.hotwire.core.navigation.session.NavigatorHost +import dev.hotwire.core.navigation.navigator.NavigatorHost import dev.hotwire.core.turbo.config.title import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate import dev.hotwire.core.turbo.nav.HotwireNavDestination import dev.hotwire.core.turbo.nav.HotwireNavDialogDestination -import dev.hotwire.core.turbo.nav.Navigator +import dev.hotwire.core.navigation.navigator.Navigator /** * The base class from which all bottom sheet native fragments in a diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt index 6542b4c..1b88a2f 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/fragments/TurboFragment.kt @@ -6,12 +6,12 @@ import android.view.View import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import dev.hotwire.core.R -import dev.hotwire.core.navigation.session.NavigatorHost +import dev.hotwire.core.navigation.navigator.NavigatorHost import dev.hotwire.core.turbo.config.context import dev.hotwire.core.turbo.config.title import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.nav.Navigator +import dev.hotwire.core.navigation.navigator.Navigator import dev.hotwire.core.turbo.nav.TurboNavPresentationContext import dev.hotwire.core.turbo.observers.HotwireWindowThemeObserver import dev.hotwire.core.turbo.session.SessionModalResult diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt index 436e0f6..2c382d1 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt @@ -11,6 +11,7 @@ import dev.hotwire.core.R import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.navigation.activities.HotwireActivity +import dev.hotwire.core.navigation.navigator.Navigator import dev.hotwire.core.navigation.routing.Router import dev.hotwire.core.turbo.config.PathConfigurationProperties import dev.hotwire.core.turbo.config.context diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt index 9835559..89911a6 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDialogDestination.kt @@ -1,34 +1,5 @@ package dev.hotwire.core.turbo.nav -import android.content.Intent -import android.os.Bundle -import androidx.activity.result.ActivityResultLauncher -import androidx.annotation.IdRes -import androidx.appcompat.widget.Toolbar -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.navigation.NavController -import androidx.navigation.NavOptions -import androidx.navigation.fragment.FragmentNavigator -import androidx.navigation.fragment.findNavController -import androidx.navigation.navOptions -import dev.hotwire.core.R -import dev.hotwire.core.config.Hotwire -import dev.hotwire.core.config.Hotwire.pathConfiguration -import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.routing.Router -import dev.hotwire.core.navigation.session.NavigatorHost -import dev.hotwire.core.turbo.config.PathConfigurationProperties -import dev.hotwire.core.turbo.config.context -import dev.hotwire.core.turbo.delegates.TurboFragmentDelegate -import dev.hotwire.core.turbo.delegates.TurboNestedFragmentDelegate -import dev.hotwire.core.turbo.fragments.TurboFragment -import dev.hotwire.core.turbo.fragments.TurboFragmentViewModel -import dev.hotwire.core.turbo.fragments.TurboWebFragment -import dev.hotwire.core.turbo.session.Session -import dev.hotwire.core.turbo.visit.VisitAction -import dev.hotwire.core.turbo.visit.VisitOptions - /** * The interface that a navigable DialogFragment implements to provide the library with * the information it needs to properly navigate. diff --git a/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt b/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt index 7dae85c..7615720 100644 --- a/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/bridge/BridgeDelegateTest.kt @@ -9,7 +9,7 @@ import com.nhaarman.mockito_kotlin.never import com.nhaarman.mockito_kotlin.whenever import dev.hotwire.core.config.Hotwire import dev.hotwire.core.turbo.nav.HotwireNavDestination -import dev.hotwire.core.turbo.nav.Navigator +import dev.hotwire.core.navigation.navigator.Navigator import dev.hotwire.core.turbo.session.Session import org.junit.Assert.* import org.junit.Before diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt index 3b72fdc..9f91c38 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/AppNavigationRouteTest.kt @@ -1,6 +1,6 @@ package dev.hotwire.core.navigation.routing -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt index 5faf243..d8089f3 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserRouteTest.kt @@ -1,6 +1,6 @@ package dev.hotwire.core.navigation.routing -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt index d4267ee..07b372b 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/routing/BrowserTabRouteTest.kt @@ -1,6 +1,6 @@ package dev.hotwire.core.navigation.routing -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith diff --git a/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt b/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt index 39f8577..f3cacfd 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt @@ -2,7 +2,7 @@ package dev.hotwire.demo.main import android.os.Bundle import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.navigation.session.NavigatorConfiguration +import dev.hotwire.core.navigation.navigator.NavigatorConfiguration import dev.hotwire.demo.R import dev.hotwire.demo.Urls diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml index aa2ad1e..58448b6 100644 --- a/demo/src/main/res/layout/activity_main.xml +++ b/demo/src/main/res/layout/activity_main.xml @@ -9,7 +9,7 @@ diff --git a/docs/QUICK-START.md b/docs/QUICK-START.md index 1d1cff0..1393b52 100644 --- a/docs/QUICK-START.md +++ b/docs/QUICK-START.md @@ -37,7 +37,7 @@ Android Jetpack provides a [`FragmentContainerView`](https://developer.android.c From 3641f770350e25b715c12b4e66ca8d3e383c5273 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 08:22:41 -0400 Subject: [PATCH 09/14] Host the configuration directly in the Navigator instance --- .../dev/hotwire/core/navigation/navigator/Navigator.kt | 7 +++++-- .../hotwire/core/navigation/navigator/NavigatorHost.kt | 10 +++++----- .../hotwire/core/turbo/nav/HotwireNavDestination.kt | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt index 60ef4a0..3eb4401 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt @@ -20,7 +20,10 @@ import dev.hotwire.core.turbo.util.location import dev.hotwire.core.turbo.visit.VisitAction import dev.hotwire.core.turbo.visit.VisitOptions -class Navigator(val host: NavigatorHost) { +class Navigator( + val host: NavigatorHost, + val configuration: NavigatorConfiguration +) { private val navController = host.navController /** @@ -50,7 +53,7 @@ class Navigator(val host: NavigatorHost) { private set internal fun createNewSession() = Session( - sessionName = host.configuration.name, + sessionName = configuration.name, activity = host.activity, webView = Hotwire.config.makeCustomWebView(host.requireContext()) ).also { diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt index ede0732..82c5678 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt @@ -13,16 +13,12 @@ open class NavigatorHost : NavHostFragment() { lateinit var navigator: Navigator private set - val configuration get() = activity.navigatorConfigurations().first { - id == it.navigatorHostId - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) activity = requireActivity() as HotwireActivity activity.delegate.registerNavigatorHost(this) - navigator = Navigator(this) + navigator = Navigator(this, configuration) initControllerGraph() } @@ -43,4 +39,8 @@ open class NavigatorHost : NavHostFragment() { ) } } + + private val configuration get() = activity.navigatorConfigurations().firstOrNull { + id == it.navigatorHostId + } ?: throw IllegalStateException("No configuration found for NavigatorHost") } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt index 2c382d1..0fb38ea 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/HotwireNavDestination.kt @@ -122,7 +122,7 @@ interface HotwireNavDestination { fun route(newLocation: String): Router.RouteResult { return Hotwire.router.route( location = newLocation, - configuration = navigator.host.configuration, + configuration = navigator.configuration, activity = fragment.requireActivity() as HotwireActivity ) } From f22a4ab0d7b4fc373c78f08dd9945bcdeaccad50 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 08:30:23 -0400 Subject: [PATCH 10/14] Rename TurboNavRule -> NavigatorRule --- .../core/navigation/navigator/Navigator.kt | 20 +++++++++---------- .../navigator/NavigatorRule.kt} | 8 +++++--- .../navigator/NavigatorRuleTest.kt} | 9 +++++---- 3 files changed, 19 insertions(+), 18 deletions(-) rename core/src/main/kotlin/dev/hotwire/core/{turbo/nav/TurboNavRule.kt => navigation/navigator/NavigatorRule.kt} (98%) rename core/src/test/kotlin/dev/hotwire/core/{turbo/nav/TurboNavRuleTest.kt => navigation/navigator/NavigatorRuleTest.kt} (99%) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt index 3eb4401..435c8ca 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt @@ -13,8 +13,6 @@ import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.lib.logging.logEvent import dev.hotwire.core.navigation.routing.Router import dev.hotwire.core.turbo.nav.* -import dev.hotwire.core.turbo.nav.TurboNavMode -import dev.hotwire.core.turbo.nav.TurboNavRule import dev.hotwire.core.turbo.session.Session import dev.hotwire.core.turbo.util.location import dev.hotwire.core.turbo.visit.VisitAction @@ -115,7 +113,7 @@ class Navigator( return } - val rule = TurboNavRule( + val rule = NavigatorRule( location = location, visitOptions = options, bundle = bundle, @@ -208,7 +206,7 @@ class Navigator( currentDestination.prepareNavigation(onReady) } - private fun navigateWithinContext(rule: TurboNavRule) { + private fun navigateWithinContext(rule: NavigatorRule) { logEvent( "navigateWithinContext", "location" to rule.newLocation, @@ -238,7 +236,7 @@ class Navigator( } } - private fun navigateToModalContext(rule: TurboNavRule) { + private fun navigateToModalContext(rule: NavigatorRule) { logEvent( "navigateToModalContext", "location" to rule.newLocation @@ -255,7 +253,7 @@ class Navigator( } } - private fun dismissModalContextWithResult(rule: TurboNavRule) { + private fun dismissModalContextWithResult(rule: NavigatorRule) { logEvent( "dismissModalContextWithResult", "location" to rule.newLocation, @@ -279,7 +277,7 @@ class Navigator( } } - private fun popModalsFromBackStack(rule: TurboNavRule) { + private fun popModalsFromBackStack(rule: NavigatorRule) { do { popBackStack(rule) } while ( @@ -287,7 +285,7 @@ class Navigator( ) } - private fun popBackStack(rule: TurboNavRule) { + private fun popBackStack(rule: NavigatorRule) { logEvent( "popFromBackStack", "location" to rule.controller.currentBackStackEntry.location.orEmpty() @@ -295,7 +293,7 @@ class Navigator( rule.controller.popBackStack() } - private fun sendModalResult(rule: TurboNavRule) { + private fun sendModalResult(rule: NavigatorRule) { // Save the modal result with VisitOptions so it can be retrieved // by the previous destination when the backstack is popped. currentDestination.delegate().sessionViewModel.sendModalResult( @@ -303,7 +301,7 @@ class Navigator( ) } - private fun replaceRootLocation(rule: TurboNavRule) { + private fun replaceRootLocation(rule: NavigatorRule) { if (rule.newDestination == null) { logEvent( "replaceRootLocation", @@ -322,7 +320,7 @@ class Navigator( rule.controller.navigate(rule.newDestination.id, rule.newBundle, rule.newNavOptions) } - private fun navigateToLocation(rule: TurboNavRule) { + private fun navigateToLocation(rule: NavigatorRule) { // Save the VisitOptions so it can be retrieved by the next // destination. When response.responseHTML is present it is // too large to save directly within the args bundle. diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavRule.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt similarity index 98% rename from core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavRule.kt rename to core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt index 16b00af..5b808ac 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavRule.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt @@ -1,4 +1,4 @@ -package dev.hotwire.core.turbo.nav +package dev.hotwire.core.navigation.navigator import android.net.Uri import android.os.Bundle @@ -9,13 +9,14 @@ import androidx.navigation.NavOptions import androidx.navigation.fragment.FragmentNavigator import androidx.navigation.navOptions import dev.hotwire.core.turbo.config.* +import dev.hotwire.core.turbo.nav.* import dev.hotwire.core.turbo.session.SessionModalResult import dev.hotwire.core.turbo.util.location import dev.hotwire.core.turbo.visit.VisitAction import dev.hotwire.core.turbo.visit.VisitOptions @Suppress("MemberVisibilityCanBePrivate") -internal class TurboNavRule( +internal class NavigatorRule( location: String, visitOptions: VisitOptions, bundle: Bundle?, @@ -129,7 +130,8 @@ internal class TurboNavRule( private fun verifyNavRules() { if (newPresentationContext == TurboNavPresentationContext.MODAL && - newPresentation == TurboNavPresentation.REPLACE_ROOT) { + newPresentation == TurboNavPresentation.REPLACE_ROOT + ) { throw TurboNavException("A `modal` destination cannot use presentation `REPLACE_ROOT`") } } diff --git a/core/src/test/kotlin/dev/hotwire/core/turbo/nav/TurboNavRuleTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt similarity index 99% rename from core/src/test/kotlin/dev/hotwire/core/turbo/nav/TurboNavRuleTest.kt rename to core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt index 05224ea..0b87643 100644 --- a/core/src/test/kotlin/dev/hotwire/core/turbo/nav/TurboNavRuleTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt @@ -1,4 +1,4 @@ -package dev.hotwire.core.turbo.nav +package dev.hotwire.core.navigation.navigator import android.content.Context import android.net.Uri @@ -13,6 +13,7 @@ import androidx.navigation.testing.TestNavHostController import androidx.navigation.ui.R import androidx.test.core.app.ApplicationProvider import dev.hotwire.core.turbo.config.PathConfiguration +import dev.hotwire.core.turbo.nav.* import dev.hotwire.core.turbo.visit.VisitOptions import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -25,7 +26,7 @@ import org.robolectric.annotation.Config @Suppress("UsePropertyAccessSyntax") @RunWith(RobolectricTestRunner::class) @Config(sdk = [Build.VERSION_CODES.R]) -class TurboNavRuleTest { +class NavigatorRuleTest { private lateinit var context: Context private lateinit var controller: TestNavHostController private lateinit var pathConfiguration: PathConfiguration @@ -384,8 +385,8 @@ class TurboNavRuleTest { location: String, visitOptions: VisitOptions = VisitOptions(), bundle: Bundle? = null - ): TurboNavRule { - return TurboNavRule( + ): NavigatorRule { + return NavigatorRule( location, visitOptions, bundle, navOptions, extras, pathConfiguration, controller ) } From 3f11b0ae18b0d0d7ca535dd5faf2099e2bc7d46c Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 08:36:28 -0400 Subject: [PATCH 11/14] Rename TurboNavMode -> NavigatorMode --- .../core/navigation/navigator/Navigator.kt | 10 +++---- .../navigation/navigator/NavigatorMode.kt | 5 ++++ .../navigation/navigator/NavigatorRule.kt | 14 +++++----- .../hotwire/core/turbo/nav/TurboNavMode.kt | 5 ---- .../navigation/navigator/NavigatorRuleTest.kt | 26 +++++++++---------- 5 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorMode.kt delete mode 100644 core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavMode.kt diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt index 435c8ca..7ba25d0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt @@ -132,19 +132,19 @@ class Navigator( ) when (rule.newNavigationMode) { - TurboNavMode.DISMISS_MODAL -> { + NavigatorMode.DISMISS_MODAL -> { dismissModalContextWithResult(rule) } - TurboNavMode.TO_MODAL -> { + NavigatorMode.TO_MODAL -> { navigateToModalContext(rule) } - TurboNavMode.IN_CONTEXT -> { + NavigatorMode.IN_CONTEXT -> { navigateWithinContext(rule) } - TurboNavMode.REFRESH -> { + NavigatorMode.REFRESH -> { route(rule.currentLocation, VisitOptions()) } - TurboNavMode.NONE -> { + NavigatorMode.NONE -> { // Do nothing } } diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorMode.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorMode.kt new file mode 100644 index 0000000..ce511ae --- /dev/null +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorMode.kt @@ -0,0 +1,5 @@ +package dev.hotwire.core.navigation.navigator + +internal enum class NavigatorMode { + IN_CONTEXT, TO_MODAL, DISMISS_MODAL, REFRESH, NONE +} diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt index 5b808ac..4625884 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt @@ -94,7 +94,7 @@ internal class NavigatorRule( return navOptions } - private fun newNavigationMode(): TurboNavMode { + private fun newNavigationMode(): NavigatorMode { val presentationNone = newPresentation == TurboNavPresentation.NONE val presentationRefresh = newPresentation == TurboNavPresentation.REFRESH @@ -107,16 +107,16 @@ internal class NavigatorRule( newPresentation != TurboNavPresentation.REPLACE_ROOT return when { - dismissModalContext -> TurboNavMode.DISMISS_MODAL - navigateToModalContext -> TurboNavMode.TO_MODAL - presentationRefresh -> TurboNavMode.REFRESH - presentationNone -> TurboNavMode.NONE - else -> TurboNavMode.IN_CONTEXT + dismissModalContext -> NavigatorMode.DISMISS_MODAL + navigateToModalContext -> NavigatorMode.TO_MODAL + presentationRefresh -> NavigatorMode.REFRESH + presentationNone -> NavigatorMode.NONE + else -> NavigatorMode.IN_CONTEXT } } private fun newModalResult(): SessionModalResult? { - if (newNavigationMode != TurboNavMode.DISMISS_MODAL) { + if (newNavigationMode != NavigatorMode.DISMISS_MODAL) { return null } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavMode.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavMode.kt deleted file mode 100644 index 915c835..0000000 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavMode.kt +++ /dev/null @@ -1,5 +0,0 @@ -package dev.hotwire.core.turbo.nav - -internal enum class TurboNavMode { - IN_CONTEXT, TO_MODAL, DISMISS_MODAL, REFRESH, NONE -} diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt index 0b87643..b3ba17c 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt @@ -86,7 +86,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.PUSH) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.REPLACE) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() @@ -108,7 +108,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.MODAL) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.PUSH) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.TO_MODAL) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.TO_MODAL) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webModalUri) assertThat(rule.newDestination).isNotNull() @@ -138,7 +138,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.CLEAR_ALL) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webHomeUri) assertThat(rule.newDestination).isNotNull() @@ -161,7 +161,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.REPLACE_ROOT) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webHomeUri) assertThat(rule.newDestination).isNotNull() @@ -188,7 +188,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.POP) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.REPLACE) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.DISMISS_MODAL) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.DISMISS_MODAL) assertThat(rule.newModalResult?.location).isEqualTo(featureUrl) assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() @@ -211,7 +211,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.MODAL) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.REPLACE) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webModalUri) assertThat(rule.newDestination).isNotNull() @@ -234,7 +234,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.MODAL) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.PUSH) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webModalUri) assertThat(rule.newDestination).isNotNull() @@ -257,7 +257,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.REFRESH) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.REFRESH) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.REFRESH) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() @@ -281,7 +281,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.NONE) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.DISMISS_MODAL) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.DISMISS_MODAL) assertThat(rule.newModalResult).isNotNull() assertThat(rule.newModalResult?.location).isEqualTo(resumeUrl) assertThat(rule.newModalResult?.shouldNavigate).isFalse() @@ -306,7 +306,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.PUSH) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() @@ -329,7 +329,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.REPLACE) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() @@ -352,7 +352,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.REPLACE) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.REPLACE) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.IN_CONTEXT) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() @@ -374,7 +374,7 @@ class NavigatorRuleTest { assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT) assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.NONE) assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT) - assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.NONE) + assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode.NONE) assertThat(rule.newModalResult).isNull() assertThat(rule.newDestinationUri).isEqualTo(webUri) assertThat(rule.newDestination).isNotNull() From 4d7c4ed1d4aebd92994055901ab1f3f61cf77b9d Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 10:27:43 -0400 Subject: [PATCH 12/14] Remove navigator.popUp() since it shouldn't be necessary to distinguish between pop() and popUp(). --- .../hotwire/core/navigation/navigator/Navigator.kt | 14 -------------- .../core/turbo/delegates/TurboFragmentDelegate.kt | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt index 7ba25d0..0cdf97f 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/Navigator.kt @@ -65,20 +65,6 @@ class Navigator( return navController.previousBackStackEntry == null } - /** - * Pops the backstack up to the previous destination. - */ - fun popUp() { - navigateWhenReady { - val currentFragment = currentDestination.fragment - if (currentFragment is HotwireNavDialogDestination) { - currentFragment.closeDialog() - } else { - navController.navigateUp() - } - } - } - /** * Pops the backstack to the previous destination. */ diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt index 05ec5b8..a5365df 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt +++ b/core/src/main/kotlin/dev/hotwire/core/turbo/delegates/TurboFragmentDelegate.kt @@ -102,7 +102,7 @@ class TurboFragmentDelegate(private val navDestination: HotwireNavDestination) { } it.setNavigationOnClickListener { - navigator.popUp() + navigator.pop() } } } From 1694e1525a46445239ce57f5a71221a69b9b5dd0 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 21 May 2024 10:43:32 -0400 Subject: [PATCH 13/14] Rename TurboNavException -> NavigatorException --- .../hotwire/core/navigation/navigator/NavigatorException.kt | 3 +++ .../dev/hotwire/core/navigation/navigator/NavigatorRule.kt | 2 +- .../kotlin/dev/hotwire/core/turbo/nav/TurboNavException.kt | 3 --- .../dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt | 2 +- demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt | 2 +- demo/src/main/res/layout/activity_main.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorException.kt delete mode 100644 core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavException.kt diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorException.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorException.kt new file mode 100644 index 0000000..feb40f9 --- /dev/null +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorException.kt @@ -0,0 +1,3 @@ +package dev.hotwire.core.navigation.navigator + +class NavigatorException(message: String) : Exception(message) diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt index 4625884..68c03e8 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRule.kt @@ -132,7 +132,7 @@ internal class NavigatorRule( if (newPresentationContext == TurboNavPresentationContext.MODAL && newPresentation == TurboNavPresentation.REPLACE_ROOT ) { - throw TurboNavException("A `modal` destination cannot use presentation `REPLACE_ROOT`") + throw NavigatorException("A `modal` destination cannot use presentation `REPLACE_ROOT`") } } diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavException.kt b/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavException.kt deleted file mode 100644 index 2cd66b5..0000000 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package dev.hotwire.core.turbo.nav - -class TurboNavException(message: String) : Exception(message) diff --git a/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt b/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt index b3ba17c..9521880 100644 --- a/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt +++ b/core/src/test/kotlin/dev/hotwire/core/navigation/navigator/NavigatorRuleTest.kt @@ -118,7 +118,7 @@ class NavigatorRuleTest { @Test fun `navigate to modal context replacing root`() { assertThatThrownBy { getNavigatorRule(modalRootUrl) } - .isInstanceOf(TurboNavException::class.java) + .isInstanceOf(NavigatorException::class.java) .hasMessage("A `modal` destination cannot use presentation `REPLACE_ROOT`") } diff --git a/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt b/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt index f3cacfd..87902b9 100644 --- a/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt +++ b/demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt @@ -16,7 +16,7 @@ class MainActivity : HotwireActivity() { NavigatorConfiguration( name = "main", startLocation = Urls.homeUrl, - navigatorHostId = R.id.main_nav_host + navigatorHostId = R.id.main_navigator_host ) ) } diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml index 58448b6..d33ea25 100644 --- a/demo/src/main/res/layout/activity_main.xml +++ b/demo/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ tools:context=".main.MainActivity"> Date: Tue, 21 May 2024 11:22:35 -0400 Subject: [PATCH 14/14] Rename TurboNavGraphBuilder -> NavigatorGraphBuilder --- .../navigator/NavigatorGraphBuilder.kt} | 5 +++-- .../dev/hotwire/core/navigation/navigator/NavigatorHost.kt | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) rename core/src/main/kotlin/dev/hotwire/core/{turbo/nav/TurboNavGraphBuilder.kt => navigation/navigator/NavigatorGraphBuilder.kt} (96%) diff --git a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorGraphBuilder.kt similarity index 96% rename from core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt rename to core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorGraphBuilder.kt index 1fa9c21..64e25e0 100644 --- a/core/src/main/kotlin/dev/hotwire/core/turbo/nav/TurboNavGraphBuilder.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorGraphBuilder.kt @@ -1,4 +1,4 @@ -package dev.hotwire.core.turbo.nav +package dev.hotwire.core.navigation.navigator import android.net.Uri import androidx.core.net.toUri @@ -11,11 +11,12 @@ import androidx.navigation.fragment.FragmentNavigator import androidx.navigation.fragment.FragmentNavigatorDestinationBuilder import dev.hotwire.core.turbo.config.PathConfiguration import dev.hotwire.core.turbo.config.uri +import dev.hotwire.core.turbo.nav.HotwireDestination import java.util.UUID import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf -internal class TurboNavGraphBuilder( +internal class NavigatorGraphBuilder( private val startLocation: String, private val navController: NavController, private val pathConfiguration: PathConfiguration diff --git a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt index 82c5678..ca69093 100644 --- a/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt +++ b/core/src/main/kotlin/dev/hotwire/core/navigation/navigator/NavigatorHost.kt @@ -6,7 +6,6 @@ import androidx.navigation.fragment.findNavController import dev.hotwire.core.config.Hotwire import dev.hotwire.core.config.Hotwire.pathConfiguration import dev.hotwire.core.navigation.activities.HotwireActivity -import dev.hotwire.core.turbo.nav.TurboNavGraphBuilder open class NavigatorHost : NavHostFragment() { internal lateinit var activity: HotwireActivity @@ -30,7 +29,7 @@ open class NavigatorHost : NavHostFragment() { internal fun initControllerGraph() { navController.apply { - graph = TurboNavGraphBuilder( + graph = NavigatorGraphBuilder( startLocation = configuration.startLocation, pathConfiguration = pathConfiguration, navController = findNavController()