Refactor navigation

This commit is contained in:
sadellie 2023-08-03 10:36:10 +03:00
parent b31bb7da73
commit 6236a1baec
13 changed files with 140 additions and 101 deletions

View File

@ -32,12 +32,11 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.base.TOP_LEVEL_START_ROUTES
import com.sadellie.unitto.core.ui.common.UnittoDrawerSheet
import com.sadellie.unitto.core.ui.common.UnittoModalNavigationDrawer
import com.sadellie.unitto.core.ui.common.close
@ -88,16 +87,9 @@ internal fun UnittoApp(uiPrefs: UIPreferences) {
val additionalTabs = listOf(DrawerItems.Settings)
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute: TopLevelDestinations? by remember(navBackStackEntry?.destination) {
val gesturesEnabled: Boolean by remember(navBackStackEntry?.destination) {
derivedStateOf {
val hierarchyRoutes = navBackStackEntry?.destination?.hierarchy?.map { it.route }
?: emptySequence()
(mainTabs + additionalTabs)
.map { it.destination }
.firstOrNull {
hierarchyRoutes.contains(it.route)
}
TOP_LEVEL_START_ROUTES.contains(navBackStackEntry?.destination?.route)
}
}
@ -117,7 +109,7 @@ internal fun UnittoApp(uiPrefs: UIPreferences) {
modifier = Modifier,
mainTabs = mainTabs,
additionalTabs = additionalTabs,
currentDestination = currentRoute
currentDestination = navBackStackEntry?.destination?.route
) {
drawerScope.launch { drawerState.close() }
navController.navigate(it) {
@ -125,12 +117,13 @@ internal fun UnittoApp(uiPrefs: UIPreferences) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
},
modifier = Modifier,
state = drawerState,
gesturesEnabled = true,
gesturesEnabled = gesturesEnabled,
scope = drawerScope,
content = {
UnittoNavigation(

View File

@ -25,10 +25,10 @@ import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import com.sadellie.unitto.feature.calculator.navigation.calculatorScreen
import com.sadellie.unitto.feature.calculator.navigation.calculatorGraph
import com.sadellie.unitto.feature.converter.ConverterViewModel
import com.sadellie.unitto.feature.converter.navigation.converterScreen
import com.sadellie.unitto.feature.datedifference.navigation.dateDifferenceScreen
import com.sadellie.unitto.feature.converter.navigation.converterGraph
import com.sadellie.unitto.feature.datedifference.navigation.dateDifferenceGraph
import com.sadellie.unitto.feature.settings.navigation.navigateToSettings
import com.sadellie.unitto.feature.settings.navigation.navigateToUnitGroups
import com.sadellie.unitto.feature.settings.navigation.settingGraph
@ -37,7 +37,7 @@ import com.sadellie.unitto.feature.unitslist.navigation.leftScreen
import com.sadellie.unitto.feature.unitslist.navigation.navigateToLeftSide
import com.sadellie.unitto.feature.unitslist.navigation.navigateToRightSide
import com.sadellie.unitto.feature.unitslist.navigation.rightScreen
import com.sadellie.unitto.timezone.navigation.timeZoneScreen
import com.sadellie.unitto.timezone.navigation.timeZoneGraph
import io.github.sadellie.themmo.ThemmoController
@Composable
@ -55,7 +55,7 @@ internal fun UnittoNavigation(
startDestination = startDestination,
modifier = Modifier.background(MaterialTheme.colorScheme.background)
) {
converterScreen(
converterGraph(
navigateToLeftScreen = navController::navigateToLeftSide,
navigateToRightScreen = navController::navigateToRightSide,
navigateToSettings = navController::navigateToSettings,
@ -83,17 +83,17 @@ internal fun UnittoNavigation(
menuButtonClick = openDrawer
)
calculatorScreen(
calculatorGraph(
navigateToMenu = openDrawer,
navigateToSettings = navController::navigateToSettings
)
dateDifferenceScreen(
dateDifferenceGraph(
navigateToMenu = openDrawer,
navigateToSettings = navController::navigateToSettings
)
timeZoneScreen(
timeZoneGraph(
navigateToMenu = openDrawer,
navigateToSettings = navController::navigateToSettings,
navController = navController,

View File

@ -20,40 +20,74 @@ package com.sadellie.unitto.core.base
import androidx.annotation.StringRes
// Don't touch, users have "..._route" in their settings
private const val CONVERTER_GRAPH = "converter_route"
private const val CONVERTER_START = "converter_start"
private const val CALCULATOR_GRAPH = "calculator_route"
private const val CALCULATOR_START = "calculator_start"
private const val DATE_CALCULATOR_GRAPH = "date_calculator_route"
private const val DATE_CALCULATOR_START = "date_calculator_start"
private const val TIME_ZONE_GRAPH = "time_zone_route"
private const val TIME_ZONE_START = "time_zone_start"
private const val SETTINGS_GRAPH = "settings_route"
private const val SETTINGS_START = "settings_start"
sealed class TopLevelDestinations(
val route: String,
@StringRes val name: Int
val graph: String,
val start: String = graph,
@StringRes val name: Int,
) {
object Converter : TopLevelDestinations(
route = "converter_route",
data object Converter : TopLevelDestinations(
graph = CONVERTER_GRAPH,
start = CONVERTER_START,
name = R.string.unit_converter
)
object Calculator : TopLevelDestinations(
route = "calculator_route",
data object Calculator : TopLevelDestinations(
graph = CALCULATOR_GRAPH,
start = CALCULATOR_START,
name = R.string.calculator
)
object DateDifference : TopLevelDestinations(
route = "date_difference_route",
data object DateDifference : TopLevelDestinations(
graph = DATE_CALCULATOR_GRAPH,
start = DATE_CALCULATOR_START,
name = R.string.date_calculator
)
object TimeZone : TopLevelDestinations(
route = "time_zone_graph",
data object TimeZone : TopLevelDestinations(
graph = TIME_ZONE_GRAPH,
start = TIME_ZONE_START,
name = R.string.time_zone_screen
)
object Settings : TopLevelDestinations(
route = "settings_graph",
data object Settings : TopLevelDestinations(
graph = SETTINGS_GRAPH,
start = SETTINGS_START,
name = R.string.settings_screen
)
}
val TOP_LEVEL_DESTINATIONS: Map<TopLevelDestinations, Int> by lazy {
// Shown in settings
val TOP_LEVEL_GRAPH_ROUTES: Map<String, Int> by lazy {
mapOf(
TopLevelDestinations.Calculator to R.string.calculator,
TopLevelDestinations.Converter to R.string.unit_converter,
TopLevelDestinations.DateDifference to R.string.date_calculator,
TopLevelDestinations.Calculator.graph to R.string.calculator,
TopLevelDestinations.Converter.graph to R.string.unit_converter,
TopLevelDestinations.DateDifference.graph to R.string.date_calculator,
)
}
// Only routes, not graphs!
val TOP_LEVEL_START_ROUTES by lazy {
listOf(
CONVERTER_START,
CALCULATOR_START,
DATE_CALCULATOR_START,
TIME_ZONE_START,
SETTINGS_START,
)
}

View File

@ -40,6 +40,6 @@ internal fun UnittoDrawerItem(
label = { Text(stringResource(destination.name)) },
icon = { Icon(icon, stringResource(destination.name)) },
selected = selected,
onClick = { onClick(destination.route) }
onClick = { onClick(destination.graph) }
)
}

View File

@ -32,7 +32,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.ui.model.DrawerItems
@Composable
@ -40,7 +39,7 @@ fun UnittoDrawerSheet(
modifier: Modifier,
mainTabs: List<DrawerItems>,
additionalTabs: List<DrawerItems>,
currentDestination: TopLevelDestinations?,
currentDestination: String?,
onItemClick: (String) -> Unit
) {
ModalDrawerSheet(
@ -54,7 +53,7 @@ fun UnittoDrawerSheet(
)
mainTabs.forEach { drawerItem ->
val selected = drawerItem.destination == currentDestination
val selected = drawerItem.destination.start == currentDestination
UnittoDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = drawerItem.destination,
@ -67,7 +66,7 @@ fun UnittoDrawerSheet(
Divider(Modifier.padding(28.dp, 16.dp))
additionalTabs.forEach { drawerItem ->
val selected = drawerItem.destination == currentDestination
val selected = drawerItem.destination.start == currentDestination
UnittoDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = drawerItem.destination,
@ -94,7 +93,7 @@ private fun PreviewUnittoDrawerSheet() {
DrawerItems.Calculator,
DrawerItems.Calculator,
),
currentDestination = DrawerItems.Calculator.destination,
currentDestination = DrawerItems.Calculator.destination.start,
onItemClick = {}
)
}

View File

@ -173,7 +173,7 @@ private fun PreviewUnittoModalNavigationDrawer() {
DrawerItems.Calculator,
DrawerItems.Calculator,
),
currentDestination = DrawerItems.Calculator.destination,
currentDestination = DrawerItems.Calculator.destination.start,
onItemClick = {}
)
},

View File

@ -78,7 +78,7 @@ data class UserPreferences(
val shownUnitGroups: List<UnitGroup> = ALL_UNIT_GROUPS,
val enableVibrations: Boolean = true,
val enableToolsExperiment: Boolean = false,
val startingScreen: String = TopLevelDestinations.Calculator.route,
val startingScreen: String = TopLevelDestinations.Calculator.graph,
val radianMode: Boolean = true,
val unitConverterFavoritesOnly: Boolean = false,
val unitConverterFormatTime: Boolean = false,
@ -92,7 +92,7 @@ data class UIPreferences(
val enableAmoledTheme: Boolean = false,
val customColor: Color = Color.Unspecified,
val monetMode: MonetMode = MonetMode.TONAL_SPOT,
val startingScreen: String = TopLevelDestinations.Converter.route,
val startingScreen: String = TopLevelDestinations.Calculator.graph,
val enableToolsExperiment: Boolean = false,
)
@ -156,7 +156,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
val customColor: Color = preferences[PrefsKeys.CUSTOM_COLOR]?.let { Color(it.toULong()) } ?: Color.Unspecified
val monetMode: MonetMode = preferences[PrefsKeys.MONET_MODE]?.let { MonetMode.valueOf(it) }
?: MonetMode.TONAL_SPOT
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Converter.route
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Calculator.graph
val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false
UIPreferences(

View File

@ -21,23 +21,28 @@ package com.sadellie.unitto.feature.calculator.navigation
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import androidx.navigation.navigation
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.calculator.CalculatorRoute
private val calculatorRoute: String by lazy { TopLevelDestinations.Calculator.route }
private val graph = TopLevelDestinations.Calculator.graph
private val start = TopLevelDestinations.Calculator.start
fun NavGraphBuilder.calculatorScreen(
fun NavGraphBuilder.calculatorGraph(
navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit
) {
composable(
route = calculatorRoute,
deepLinks = listOf(
navDeepLink { uriPattern = "app://com.sadellie.unitto/$calculatorRoute" }
)) {
CalculatorRoute(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings
)
navigation(start, graph) {
composable(
route = start,
deepLinks = listOf(
navDeepLink { uriPattern = "app://com.sadellie.unitto/$start" }
)
) {
CalculatorRoute(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings
)
}
}
}

View File

@ -20,26 +20,30 @@ package com.sadellie.unitto.feature.converter.navigation
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navigation
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.converter.ConverterRoute
import com.sadellie.unitto.feature.converter.ConverterViewModel
private val converterRoute: String by lazy { TopLevelDestinations.Converter.route }
private val graph = TopLevelDestinations.Converter.graph
private val start = TopLevelDestinations.Converter.start
fun NavGraphBuilder.converterScreen(
fun NavGraphBuilder.converterGraph(
navigateToLeftScreen: (String) -> Unit,
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String?) -> Unit,
navigateToSettings: () -> Unit,
navigateToMenu: () -> Unit,
viewModel: ConverterViewModel
) {
composable(converterRoute) {
ConverterRoute(
viewModel = viewModel,
navigateToLeftScreen = navigateToLeftScreen,
navigateToRightScreen = navigateToRightScreen,
navigateToSettings = navigateToSettings,
navigateToMenu = navigateToMenu
)
navigation(start, graph) {
composable(start) {
ConverterRoute(
viewModel = viewModel,
navigateToLeftScreen = navigateToLeftScreen,
navigateToRightScreen = navigateToRightScreen,
navigateToSettings = navigateToSettings,
navigateToMenu = navigateToMenu
)
}
}
}

View File

@ -21,24 +21,28 @@ package com.sadellie.unitto.feature.datedifference.navigation
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import androidx.navigation.navigation
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.datedifference.DateToolsRoute
private val dateDifferenceRoute: String by lazy { TopLevelDestinations.DateDifference.route }
private val graph = TopLevelDestinations.DateDifference.graph
private val start = TopLevelDestinations.DateDifference.start
fun NavGraphBuilder.dateDifferenceScreen(
fun NavGraphBuilder.dateDifferenceGraph(
navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit
) {
composable(
route = dateDifferenceRoute,
deepLinks = listOf(
navDeepLink { uriPattern = "app://com.sadellie.unitto/$dateDifferenceRoute" }
)
) {
DateToolsRoute(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings
)
navigation(start, graph) {
composable(
route = start,
deepLinks = listOf(
navDeepLink { uriPattern = "app://com.sadellie.unitto/$start" }
)
) {
DateToolsRoute(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings
)
}
}
}

View File

@ -46,7 +46,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.BuildConfig
import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.TOP_LEVEL_DESTINATIONS
import com.sadellie.unitto.core.base.TOP_LEVEL_GRAPH_ROUTES
import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.core.ui.common.MenuButton
import com.sadellie.unitto.core.ui.common.UnittoListItem
@ -252,7 +252,7 @@ internal fun SettingsScreen(
AlertDialogWithList(
title = stringResource(R.string.starting_screen_setting),
selectedItemIndex = userPrefs.value.startingScreen,
listItems = TOP_LEVEL_DESTINATIONS.mapKeys { it.key.route },
listItems = TOP_LEVEL_GRAPH_ROUTES,
selectAction = viewModel::updateStartingScreen,
dismissAction = { resetDialog() }
)

View File

@ -32,8 +32,8 @@ import com.sadellie.unitto.feature.settings.themes.ThemesRoute
import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsScreen
import io.github.sadellie.themmo.ThemmoController
private val settingsGraph: String by lazy { TopLevelDestinations.Settings.route }
private const val settingsRoute = "settings_route"
private val graph = TopLevelDestinations.Settings.graph
private val start = TopLevelDestinations.Settings.start
internal const val themesRoute = "themes_route"
internal const val unitsGroupRoute = "units_group_route"
internal const val thirdPartyRoute = "third_party_route"
@ -41,7 +41,7 @@ internal const val aboutRoute = "about_route"
internal const val formattingRoute = "formatting_route"
fun NavController.navigateToSettings() {
navigate(settingsRoute)
navigate(TopLevelDestinations.Settings.start)
}
fun NavController.navigateToUnitGroups() {
@ -53,8 +53,8 @@ fun NavGraphBuilder.settingGraph(
navController: NavHostController,
menuButtonClick: () -> Unit
) {
navigation(settingsRoute, settingsGraph) {
composable(settingsRoute) {
navigation(start, graph) {
composable(start) {
SettingsScreen(
menuButtonClick = menuButtonClick,
navControllerAction = navController::navigate

View File

@ -32,32 +32,32 @@ import com.sadellie.unitto.timezone.TimeZoneRoute
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
private val timeZoneGraph: String by lazy { TopLevelDestinations.TimeZone.route }
internal const val timeZoneRoute = "time_zone_route"
internal const val addTimeZoneRoute = "add_time_zone_route"
internal const val userTimeArg = "userTime"
private val graph = TopLevelDestinations.TimeZone.start
private val start: String = TopLevelDestinations.TimeZone.graph
private const val ADD_TIME_ZONE_ROUTE = "ADD_TIME_ZONE_ROUTE"
private const val USER_TIME_ARG = "USER_TIME_ARG"
fun NavController.navigateToAddTimeZone(
private fun NavController.navigateToAddTimeZone(
userTime: ZonedDateTime?
) {
val formattedTime = userTime
?.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
?.replace("/", "|") // this is so wrong
navigate("$addTimeZoneRoute/$formattedTime")
navigate("$ADD_TIME_ZONE_ROUTE/$formattedTime")
}
fun NavGraphBuilder.timeZoneScreen(
fun NavGraphBuilder.timeZoneGraph(
navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit,
navController: NavHostController,
) {
navigation(
startDestination = timeZoneRoute,
route = timeZoneGraph,
deepLinks = listOf(navDeepLink { uriPattern = "app://com.sadellie.unitto/$timeZoneRoute" })
startDestination = start,
route = graph,
deepLinks = listOf(navDeepLink { uriPattern = "app://com.sadellie.unitto/$start" })
) {
composable(timeZoneRoute) {
composable(start) {
TimeZoneRoute(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings,
@ -66,9 +66,9 @@ fun NavGraphBuilder.timeZoneScreen(
}
composable(
route = "$addTimeZoneRoute/{$userTimeArg}",
route = "$ADD_TIME_ZONE_ROUTE/{$USER_TIME_ARG}",
arguments = listOf(
navArgument(userTimeArg) {
navArgument(USER_TIME_ARG) {
defaultValue = null
nullable = true
type = NavType.StringType
@ -76,7 +76,7 @@ fun NavGraphBuilder.timeZoneScreen(
)
) { stackEntry ->
val userTime = stackEntry.arguments
?.getString(userTimeArg)
?.getString(USER_TIME_ARG)
?.replace("|", "/") // war crime, don't look
?.let { ZonedDateTime.parse(it, DateTimeFormatter.ISO_ZONED_DATE_TIME) }