@@ -3,22 +3,17 @@ package org.jetbrains.kotlinconf.navigation
33import androidx.compose.animation.AnimatedVisibility
44import androidx.compose.animation.core.AnimationConstants
55import androidx.compose.animation.core.tween
6- import androidx.compose.animation.expandHorizontally
76import androidx.compose.animation.expandVertically
87import androidx.compose.animation.fadeOut
9- import androidx.compose.animation.shrinkHorizontally
108import androidx.compose.animation.shrinkVertically
11- import androidx.compose.foundation.background
129import androidx.compose.foundation.layout.Box
1310import androidx.compose.foundation.layout.Column
1411import androidx.compose.foundation.layout.Row
1512import androidx.compose.foundation.layout.WindowInsets
16- import androidx.compose.foundation.layout.fillMaxSize
17- import androidx.compose.foundation.layout.fillMaxWidth
1813import androidx.compose.foundation.layout.ime
1914import androidx.compose.foundation.layout.padding
2015import androidx.compose.runtime.Composable
21- import androidx.compose.runtime.remember
16+ import androidx.compose.runtime.getValue
2217import androidx.compose.runtime.rememberUpdatedState
2318import androidx.compose.ui.Modifier
2419import androidx.compose.ui.draw.clipToBounds
@@ -52,13 +47,11 @@ import org.jetbrains.kotlinconf.ui.components.MainNavigationRail
5247import org.jetbrains.kotlinconf.ui.components.Text
5348import org.jetbrains.kotlinconf.ui.components.VerticalDivider
5449import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme
55- import org.jetbrains.kotlinconf.utils.LocalNotificationBar
56- import org.jetbrains.kotlinconf.utils.LocalWindowSize
5750import org.jetbrains.kotlinconf.utils.WindowSize
5851import org.jetbrains.kotlinconf.utils.bottomInsetPadding
5952import org.jetbrains.kotlinconf.utils.topInsetPadding
6053
61- private val bottomNavDestinations : List <MainNavDestination <TopLevelRoute >> = listOf (
54+ private val topLevelNavDestinations : List <MainNavDestination <TopLevelRoute >> = listOf (
6255 MainNavDestination (
6356 label = Res .string.nav_destination_schedule,
6457 icon = Res .drawable.clock_28,
@@ -91,166 +84,98 @@ private val bottomNavDestinations: List<MainNavDestination<TopLevelRoute>> = lis
9184 ),
9285)
9386
94- internal class NavigationSceneDecoratorStrategy (
87+ internal class TopLevelNavStrategy (
9588 private val navState : NavState ,
96- private val navigator : Navigator ,
89+ private val windowSize : WindowSize ,
9790 private val showGoldenKodee : Boolean ,
91+ private val onSelectRoute : (TopLevelRoute ) -> Unit ,
9892) : SceneDecoratorStrategy<AppRoute> {
9993 override fun SceneDecoratorStrategyScope<AppRoute>.decorateScene (scene : Scene <AppRoute >): Scene <AppRoute > {
100- return if (navState.topLevelRoute != null ) {
101- NavigationDecoratingScene (scene, navState, navigator, showGoldenKodee)
94+ if (navState.topLevelRoute == null ) {
95+ return scene
96+ }
97+
98+ val destinations = if (showGoldenKodee) {
99+ topLevelNavDestinations
102100 } else {
103- scene
101+ topLevelNavDestinations.filter { it.route !is GoldenKodeeScreen }
102+ }
103+
104+ return when (windowSize) {
105+ WindowSize .Compact -> {
106+ if (navState.currentBackstack.size == 1 ) {
107+ BottomNavScene (scene, navState, onSelectRoute, destinations)
108+ } else {
109+ scene
110+ }
111+ }
112+
113+ WindowSize .Medium , WindowSize .Large -> {
114+ NavRailScene (scene, navState, onSelectRoute, destinations, windowSize)
115+ }
104116 }
105117 }
106118}
107119
108- private class NavigationDecoratingScene (
120+ private class BottomNavScene (
109121 private val scene : Scene <AppRoute >,
110122 private val navState : NavState ,
111- private val navigator : Navigator ,
112- private val showGoldenKodee : Boolean ,
123+ private val onSelectRoute : ( TopLevelRoute ) -> Unit ,
124+ private val destinations : List < MainNavDestination < TopLevelRoute >> ,
113125) : Scene<AppRoute> by scene {
114126 override val content: @Composable () -> Unit = {
115- NavScaffold (navState, navigator, showGoldenKodee, scene)
116- }
117- }
118-
119- @Composable
120- private fun NavScaffold (
121- navState : NavState ,
122- navigator : Navigator ,
123- showGoldenKodee : Boolean ,
124- scene : Scene <AppRoute >,
125- ) {
126- val onSelectRoute: (TopLevelRoute ) -> Unit = { route ->
127- navigator.activate(route)
128- }
129-
130- val destinations = remember(showGoldenKodee) {
131- if (showGoldenKodee) {
132- bottomNavDestinations
133- } else {
134- bottomNavDestinations.filter { it.route !is GoldenKodeeScreen }
135- }
136- }
137-
138- val windowSize = LocalWindowSize .current
139-
140- val showLargeNavigation = windowSize != WindowSize .Compact &&
141- navState.topLevelRoute != null
142- val showCompactNavigation = windowSize == WindowSize .Compact &&
143- navState.topLevelRoute != null &&
144- navState.currentBackstack.size == 1 &&
145- ! isKeyboardOpen()
146-
147- Row (
148- Modifier .fillMaxSize()
149- .background(color = KotlinConfTheme .colors.mainBackground)
150- ) {
151- val enterAnimSpec = tween<IntSize >(delayMillis = AnimationConstants .DefaultDurationMillis )
152-
153- AnimatedVisibility (
154- visible = showLargeNavigation,
155- enter = expandHorizontally(enterAnimSpec),
156- exit = shrinkHorizontally() + fadeOut(),
157- ) {
158- SideNavigation (
159- currentRoute = navState.topLevelRoute,
160- destinations = destinations,
161- onSelectRoute = onSelectRoute,
162- expanded = windowSize == WindowSize .Large ,
163- )
164- }
165-
166- Column (Modifier .weight(1f )) {
167- Box (Modifier .weight(1f ).clipToBounds()) {
127+ Column (Modifier .padding(bottomInsetPadding())) {
128+ Box (Modifier .weight(1f )) {
168129 scene.content()
169130 }
170131
171- NotificationBar ()
132+ val enterAnimSpec =
133+ tween<IntSize >(delayMillis = AnimationConstants .DefaultDurationMillis )
134+
135+ val bottomInset = WindowInsets .ime.getBottom(LocalDensity .current)
136+ val isKeyboardOpen by rememberUpdatedState(bottomInset > 300 )
172137
173138 AnimatedVisibility (
174- visible = showCompactNavigation ,
139+ visible = ! isKeyboardOpen ,
175140 enter = expandVertically(enterAnimSpec),
176141 exit = shrinkVertically() + fadeOut(),
177142 ) {
178- BottomNavigation (
179- currentRoute = navState.topLevelRoute,
143+ HorizontalDivider (thickness = 1 .dp, color = KotlinConfTheme .colors.strokePale)
144+
145+ MainNavigationBar (
146+ currentDestination = destinations.find { it.route == navState.topLevelRoute },
180147 destinations = destinations,
181- onSelectRoute = onSelectRoute,
148+ onSelect = { selectedDestination ->
149+ onSelectRoute(selectedDestination.route)
150+ },
182151 )
183152 }
184153 }
185154 }
186155}
187156
188- @Composable
189- private fun isKeyboardOpen (): Boolean {
190- val bottomInset = WindowInsets .ime.getBottom(LocalDensity .current)
191- return rememberUpdatedState(bottomInset > 300 ).value
192- }
193-
194- @Composable
195- private fun BottomNavigation (
196- currentRoute : TopLevelRoute ? ,
197- destinations : List <MainNavDestination <TopLevelRoute >>,
198- onSelectRoute : (TopLevelRoute ) -> Unit ,
199- ) {
200- val currentDestination = destinations.find { it.route == currentRoute }
201-
202- Column (
203- Modifier .padding(bottomInsetPadding()),
204- ) {
205- HorizontalDivider (thickness = 1 .dp, color = KotlinConfTheme .colors.strokePale)
206- MainNavigationBar (
207- currentDestination = currentDestination,
208- destinations = destinations,
209- onSelect = { selectedDestination ->
210- onSelectRoute(selectedDestination.route)
211- },
212- )
213- }
214- }
215-
216- @Composable
217- private fun SideNavigation (
218- currentRoute : TopLevelRoute ? ,
219- destinations : List <MainNavDestination <TopLevelRoute >>,
220- onSelectRoute : (TopLevelRoute ) -> Unit ,
221- expanded : Boolean ,
222- ) {
223- val currentDestination = destinations.find { it.route == currentRoute }
157+ private class NavRailScene (
158+ private val scene : Scene <AppRoute >,
159+ private val navState : NavState ,
160+ private val onSelectRoute : (TopLevelRoute ) -> Unit ,
161+ private val destinations : List <MainNavDestination <TopLevelRoute >>,
162+ private val windowSize : WindowSize ,
163+ ) : Scene<AppRoute> by scene {
164+ override val content: @Composable () -> Unit = {
165+ Row {
166+ MainNavigationRail (
167+ currentDestination = destinations.find { it.route == navState.topLevelRoute },
168+ destinations = destinations,
169+ onSelect = { selectedDestination ->
170+ onSelectRoute(selectedDestination.route)
171+ },
172+ expanded = windowSize == WindowSize .Large ,
173+ modifier = Modifier .padding(topInsetPadding()),
174+ )
224175
225- Row {
226- MainNavigationRail (
227- currentDestination = currentDestination,
228- destinations = destinations,
229- onSelect = { selectedDestination ->
230- onSelectRoute(selectedDestination.route)
231- },
232- expanded = expanded,
233- modifier = Modifier .padding(topInsetPadding()),
234- )
235- VerticalDivider (thickness = 1 .dp, color = KotlinConfTheme .colors.strokePale)
236- }
237- }
176+ VerticalDivider (thickness = 1 .dp, color = KotlinConfTheme .colors.strokePale)
238177
239- @Composable
240- private fun NotificationBar () {
241- val message = LocalNotificationBar .current.message
242- AnimatedVisibility (
243- visible = message != null ,
244- modifier = Modifier .fillMaxWidth(),
245- ) {
246- Text (
247- text = message ? : " " ,
248- color = KotlinConfTheme .colors.primaryTextInverted,
249- style = KotlinConfTheme .typography.text2.copy(textAlign = TextAlign .Center ),
250- modifier = Modifier
251- .fillMaxWidth()
252- .background(color = KotlinConfTheme .colors.mainBackgroundInverted)
253- .padding(vertical = 16 .dp, horizontal = 10 .dp)
254- )
178+ scene.content()
179+ }
255180 }
256181}
0 commit comments