Skip to content

Commit 0f93182

Browse files
committed
Move window size logic into SceneDecoratorStrategy, simplify scaffolding layouts
1 parent c5548e4 commit 0f93182

2 files changed

Lines changed: 74 additions & 142 deletions

File tree

app/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/NavHost.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import org.jetbrains.kotlinconf.ui.theme.KotlinConfDarkColors
6969
import org.jetbrains.kotlinconf.ui.theme.KotlinConfLightColors
7070
import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme
7171
import org.jetbrains.kotlinconf.utils.DateTimeFormatting
72+
import org.jetbrains.kotlinconf.utils.LocalWindowSize
7273
import org.jetbrains.kotlinconf.utils.getStoreUrl
7374
import org.jetbrains.kotlinconf.utils.topInsetPadding
7475
import org.jetbrains.kotlinconf.screens.DeveloperMenuScreen as DeveloperMenuScreenContent
@@ -143,8 +144,14 @@ internal fun NavHost(
143144
val showGoldenKodee by remember { conferenceService.goldenKodeeData.map { it != null } }
144145
.collectAsStateWithLifecycle(false)
145146

146-
val navigationDecorator = remember(navState, navigator, showGoldenKodee) {
147-
NavigationSceneDecoratorStrategy(navState, navigator, showGoldenKodee)
147+
val windowSize = LocalWindowSize.current
148+
val navigationDecorator = remember(navState, navigator, showGoldenKodee, windowSize) {
149+
TopLevelNavStrategy(
150+
navState = navState,
151+
windowSize = windowSize,
152+
showGoldenKodee = showGoldenKodee,
153+
onSelectRoute = { route -> navigator.activate(route) },
154+
)
148155
}
149156

150157
val isGoldenKodee = navState.topLevelRoute is GoldenKodeeScreen

app/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/TopLevelNavStrategy.kt

Lines changed: 65 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,17 @@ package org.jetbrains.kotlinconf.navigation
33
import androidx.compose.animation.AnimatedVisibility
44
import androidx.compose.animation.core.AnimationConstants
55
import androidx.compose.animation.core.tween
6-
import androidx.compose.animation.expandHorizontally
76
import androidx.compose.animation.expandVertically
87
import androidx.compose.animation.fadeOut
9-
import androidx.compose.animation.shrinkHorizontally
108
import androidx.compose.animation.shrinkVertically
11-
import androidx.compose.foundation.background
129
import androidx.compose.foundation.layout.Box
1310
import androidx.compose.foundation.layout.Column
1411
import androidx.compose.foundation.layout.Row
1512
import androidx.compose.foundation.layout.WindowInsets
16-
import androidx.compose.foundation.layout.fillMaxSize
17-
import androidx.compose.foundation.layout.fillMaxWidth
1813
import androidx.compose.foundation.layout.ime
1914
import androidx.compose.foundation.layout.padding
2015
import androidx.compose.runtime.Composable
21-
import androidx.compose.runtime.remember
16+
import androidx.compose.runtime.getValue
2217
import androidx.compose.runtime.rememberUpdatedState
2318
import androidx.compose.ui.Modifier
2419
import androidx.compose.ui.draw.clipToBounds
@@ -52,13 +47,11 @@ import org.jetbrains.kotlinconf.ui.components.MainNavigationRail
5247
import org.jetbrains.kotlinconf.ui.components.Text
5348
import org.jetbrains.kotlinconf.ui.components.VerticalDivider
5449
import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme
55-
import org.jetbrains.kotlinconf.utils.LocalNotificationBar
56-
import org.jetbrains.kotlinconf.utils.LocalWindowSize
5750
import org.jetbrains.kotlinconf.utils.WindowSize
5851
import org.jetbrains.kotlinconf.utils.bottomInsetPadding
5952
import 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

Comments
 (0)