diff --git a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/NatNavHost.kt b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/NatNavHost.kt index 1710937..9c180fb 100644 --- a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/NatNavHost.kt +++ b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/NatNavHost.kt @@ -31,15 +31,17 @@ import com.nativeapptemplate.nativeapptemplatefree.ui.app_root.navigation.resend import com.nativeapptemplate.nativeapptemplatefree.ui.app_root.navigation.signInEmailAndPasswordView import com.nativeapptemplate.nativeapptemplatefree.ui.app_root.navigation.signUpOrSignInView import com.nativeapptemplate.nativeapptemplatefree.ui.app_root.navigation.signUpView +import com.nativeapptemplate.nativeapptemplatefree.ui.settings.navigation.settingBaseView import com.nativeapptemplate.nativeapptemplatefree.ui.shop_detail.navigation.navigateToShopDetail import com.nativeapptemplate.nativeapptemplatefree.ui.shop_detail.navigation.shopDetailView import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToShopBasicSettings import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.navigateToShopSettings import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.shopBasicSettingsView import com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.navigation.shopSettingsView -import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.ShopsRoute +import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.ShopBaseRoute import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.navigateToShopCreate import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.navigateToShopList +import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.shopBaseView import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.shopCreateView import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.shopListView @@ -85,7 +87,7 @@ fun NatNavHost( NavHost( navController = navController, - startDestination = ShopsRoute, + startDestination = ShopBaseRoute, modifier = modifier, ) { onboardingView( @@ -123,42 +125,46 @@ fun NatNavHost( onShowSnackbar = onShowSnackbar, ) - shopListView( - onItemClick = { shopId -> navController.navigateToShopDetail(shopId) }, - onAddShopClick = { navController.navigateToShopCreate() }, - onShowSnackbar = onShowSnackbar, - ) - shopCreateView( - onShowSnackbar = onShowSnackbar, - onBackClick = navController::popBackStack, - ) - shopDetailView( - onSettingsClick = { shopId -> navController.navigateToShopSettings(shopId) }, - onShowSnackbar = onShowSnackbar, - onBackClick = navController::popBackStack, - ) - shopSettingsView( - onShowBasicSettingsClick = { shopId -> navController.navigateToShopBasicSettings(shopId) }, - onShowSnackbar = onShowSnackbar, - onBackClick = navController::popBackStack, - ) - shopBasicSettingsView( - onShowSnackbar = onShowSnackbar, - onBackClick = navController::popBackStack, - ) + shopBaseView { + shopListView( + onItemClick = { shopId -> navController.navigateToShopDetail(shopId) }, + onAddShopClick = { navController.navigateToShopCreate() }, + onShowSnackbar = onShowSnackbar, + ) + shopCreateView( + onShowSnackbar = onShowSnackbar, + onBackClick = navController::popBackStack, + ) + shopDetailView( + onSettingsClick = { shopId -> navController.navigateToShopSettings(shopId) }, + onShowSnackbar = onShowSnackbar, + onBackClick = navController::popBackStack, + ) + shopSettingsView( + onShowBasicSettingsClick = { shopId -> navController.navigateToShopBasicSettings(shopId) }, + onShowSnackbar = onShowSnackbar, + onBackClick = navController::popBackStack, + ) + shopBasicSettingsView( + onShowSnackbar = onShowSnackbar, + onBackClick = navController::popBackStack, + ) + } - settingsView( - onShowShopkeeperEditClick = { navController.navigateToShopkeeperEdit() }, - onShowPasswordEditClick = { navController.navigateToPasswordEdit() }, - onShowSnackbar = onShowSnackbar, - ) - shopkeeperEditView( - onShowSnackbar = onShowSnackbar, - onBackClick =navController::popBackStack, - ) - passwordEditView( - onShowSnackbar = onShowSnackbar, - onBackClick =navController::popBackStack, - ) + settingBaseView { + settingsView( + onShowShopkeeperEditClick = { navController.navigateToShopkeeperEdit() }, + onShowPasswordEditClick = { navController.navigateToPasswordEdit() }, + onShowSnackbar = onShowSnackbar, + ) + shopkeeperEditView( + onShowSnackbar = onShowSnackbar, + onBackClick =navController::popBackStack, + ) + passwordEditView( + onShowSnackbar = onShowSnackbar, + onBackClick =navController::popBackStack, + ) + } } } diff --git a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/TopLevelDestination.kt b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/TopLevelDestination.kt index 0368e02..5d49519 100644 --- a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/TopLevelDestination.kt +++ b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/navigation/TopLevelDestination.kt @@ -1,30 +1,17 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.nativeapptemplate.nativeapptemplatefree.navigation import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.People import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Storefront -import androidx.compose.material.icons.rounded.People import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Storefront import androidx.compose.ui.graphics.vector.ImageVector import com.nativeapptemplate.nativeapptemplatefree.R +import com.nativeapptemplate.nativeapptemplatefree.ui.settings.navigation.SettingBaseRoute +import com.nativeapptemplate.nativeapptemplatefree.ui.settings.navigation.SettingsRoute +import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.ShopBaseRoute +import com.nativeapptemplate.nativeapptemplatefree.ui.shops.navigation.ShopsRoute +import kotlin.reflect.KClass /** * Type for the top level destinations in the application. Each of these destinations @@ -32,18 +19,24 @@ import com.nativeapptemplate.nativeapptemplatefree.R * next within a single destination will be handled directly in composables. */ enum class TopLevelDestination( - val selectedIcon: ImageVector, - val unselectedIcon: ImageVector, - val iconTextId: Int, + val selectedIcon: ImageVector, + val unselectedIcon: ImageVector, + val iconTextId: Int, + val route: KClass<*>, + val baseRoute: KClass<*> = route, ) { SHOPS_TAB( selectedIcon = Icons.Rounded.Storefront, unselectedIcon = Icons.Outlined.Storefront, iconTextId = R.string.title_shops, + route = ShopsRoute::class, + baseRoute = ShopBaseRoute::class, ), SETTINGS_TAB( selectedIcon = Icons.Rounded.Settings, unselectedIcon = Icons.Outlined.Settings, iconTextId = R.string.title_settings, + route = SettingsRoute::class, + baseRoute = SettingBaseRoute::class, ), } diff --git a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/NatApp.kt b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/NatApp.kt index ff0e2c4..2d9d8c4 100644 --- a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/NatApp.kt +++ b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/NatApp.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavDestination +import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavDestination.Companion.hierarchy import com.nativeapptemplate.nativeapptemplatefree.R import com.nativeapptemplate.nativeapptemplatefree.designsystem.component.NatBackground @@ -39,6 +40,7 @@ import com.nativeapptemplate.nativeapptemplatefree.designsystem.component.NatNav import com.nativeapptemplate.nativeapptemplatefree.designsystem.component.NatNavigationBarItem import com.nativeapptemplate.nativeapptemplatefree.navigation.NatNavHost import com.nativeapptemplate.nativeapptemplatefree.navigation.TopLevelDestination +import kotlin.reflect.KClass @OptIn( ExperimentalComposeUiApi::class, @@ -162,7 +164,8 @@ private fun NatBottomBar( modifier = modifier, ) { destinations.forEach { destination -> - val selected = currentDestination.isTopLevelDestinationInHierarchy(destination) + val selected = currentDestination.isRouteInHierarchy(destination.baseRoute) + NatNavigationBarItem( selected = selected, onClick = { onNavigateToDestination(destination) }, @@ -184,7 +187,7 @@ private fun NatBottomBar( } } -private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: TopLevelDestination) = +private fun NavDestination?.isRouteInHierarchy(route: KClass<*>) = this?.hierarchy?.any { - it.route?.contains(destination.name, true) ?: false - } ?: false + it.hasRoute(route) + } ?: false \ No newline at end of file diff --git a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/navigation/SettingsNavigation.kt b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/navigation/SettingsNavigation.kt index 626b8fc..ad3a281 100644 --- a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/navigation/SettingsNavigation.kt +++ b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/navigation/SettingsNavigation.kt @@ -5,15 +5,25 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation import com.nativeapptemplate.nativeapptemplatefree.ui.settings.PasswordEditView import com.nativeapptemplate.nativeapptemplatefree.ui.settings.SettingsView import com.nativeapptemplate.nativeapptemplatefree.ui.settings.ShopkeeperEditView import kotlinx.serialization.Serializable +@Serializable data object SettingBaseRoute @Serializable data object SettingsRoute @Serializable data object ShopkeeperEditRoute @Serializable data object PasswordEditRoute +fun NavGraphBuilder.settingBaseView( + destination: NavGraphBuilder.() -> Unit, +) { + navigation(startDestination = SettingsRoute) { + destination() + } +} + fun NavController.navigateToSettings(navOptions: NavOptions) = navigate(route = SettingsRoute, navOptions) fun NavGraphBuilder.settingsView( diff --git a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shops/navigation/ShopsNavigation.kt b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shops/navigation/ShopsNavigation.kt index dca546f..b6c96df 100644 --- a/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shops/navigation/ShopsNavigation.kt +++ b/app/src/main/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shops/navigation/ShopsNavigation.kt @@ -5,13 +5,23 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation import com.nativeapptemplate.nativeapptemplatefree.ui.shops.ShopCreateView import com.nativeapptemplate.nativeapptemplatefree.ui.shops.ShopListView import kotlinx.serialization.Serializable +@Serializable data object ShopBaseRoute @Serializable data object ShopsRoute @Serializable data object ShopCreateRoute +fun NavGraphBuilder.shopBaseView( + destination: NavGraphBuilder.() -> Unit, +) { + navigation(startDestination = ShopsRoute) { + destination() + } +} + fun NavController.navigateToShopList(navOptions: NavOptions? = null) = navigate(route = ShopsRoute, navOptions) fun NavGraphBuilder.shopListView( diff --git a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/datastore/NatPreferencesDataSourceTest.kt b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/datastore/NatPreferencesDataSourceTest.kt index 1c1caa7..9eea402 100644 --- a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/datastore/NatPreferencesDataSourceTest.kt +++ b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/datastore/NatPreferencesDataSourceTest.kt @@ -47,7 +47,7 @@ private const val LOGGED_IN_SHOPKEEPER_ACCOUNT_NAME = "Account1" private const val LOGGED_IN_SHOPKEEPER_EMAIL = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_NAME = "John Smith" private const val LOGGED_IN_SHOPKEEPER_TIME_ZONE = "Tokyo" -private const val LOGGED_IN_SHOPKEEPER_TOKEN = "john@example.com" +private const val LOGGED_IN_SHOPKEEPER_TOKEN = "RVpFr8xbeiWRnj8cu7AeOP" private const val LOGGED_IN_SHOPKEEPER_CLIENT = "Vd6GFW-9DaZrU2pzFd-Asa" private const val LOGGED_IN_SHOPKEEPER_UID = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_EXPIRY = "1713165114" diff --git a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignInEmailAndPasswordViewModelTest.kt b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignInEmailAndPasswordViewModelTest.kt index 74876cc..326879e 100644 --- a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignInEmailAndPasswordViewModelTest.kt +++ b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignInEmailAndPasswordViewModelTest.kt @@ -119,7 +119,7 @@ private const val LOGGED_IN_SHOPKEEPER_ACCOUNT_NAME = "Account1" private const val LOGGED_IN_SHOPKEEPER_EMAIL = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_NAME = "John Smith" private const val LOGGED_IN_SHOPKEEPER_TIME_ZONE = "Tokyo" -private const val LOGGED_IN_SHOPKEEPER_TOKEN = "john@example.com" +private const val LOGGED_IN_SHOPKEEPER_TOKEN = "RVpFr8xbeiWRnj8cu7AeOP" private const val LOGGED_IN_SHOPKEEPER_CLIENT = "Vd6GFW-9DaZrU2pzFd-Asa" private const val LOGGED_IN_SHOPKEEPER_UID = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_EXPIRY = "1713165114" diff --git a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignUpViewModelTest.kt b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignUpViewModelTest.kt index 30d0f19..73b9839 100644 --- a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignUpViewModelTest.kt +++ b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/app_root/SignUpViewModelTest.kt @@ -122,7 +122,7 @@ private const val LOGGED_IN_SHOPKEEPER_ACCOUNT_NAME = "Account1" private const val LOGGED_IN_SHOPKEEPER_EMAIL = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_NAME = "John Smith" private const val LOGGED_IN_SHOPKEEPER_TIME_ZONE = "Tokyo" -private const val LOGGED_IN_SHOPKEEPER_TOKEN = "john@example.com" +private const val LOGGED_IN_SHOPKEEPER_TOKEN = "RVpFr8xbeiWRnj8cu7AeOP" private const val LOGGED_IN_SHOPKEEPER_CLIENT = "Vd6GFW-9DaZrU2pzFd-Asa" private const val LOGGED_IN_SHOPKEEPER_UID = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_EXPIRY = "1713165114" diff --git a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/ShopkeeperEditViewModelTest.kt b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/ShopkeeperEditViewModelTest.kt index 099a5d7..a6dee5a 100644 --- a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/ShopkeeperEditViewModelTest.kt +++ b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/settings/ShopkeeperEditViewModelTest.kt @@ -229,7 +229,7 @@ private const val LOGGED_IN_SHOPKEEPER_ACCOUNT_NAME = "Account1" private const val LOGGED_IN_SHOPKEEPER_EMAIL = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_NAME = "John Smith" private const val LOGGED_IN_SHOPKEEPER_TIME_ZONE = "Tokyo" -private const val LOGGED_IN_SHOPKEEPER_TOKEN = "john@example.com" +private const val LOGGED_IN_SHOPKEEPER_TOKEN = "RVpFr8xbeiWRnj8cu7AeOP" private const val LOGGED_IN_SHOPKEEPER_CLIENT = "Vd6GFW-9DaZrU2pzFd-Asa" private const val LOGGED_IN_SHOPKEEPER_UID = "john@example.com" private const val LOGGED_IN_SHOPKEEPER_EXPIRY = "1713165114" diff --git a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shop_settings/ShopBasicSettingsViewModelTest.kt b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shop_settings/ShopBasicSettingsViewModelTest.kt index 60272f3..671e70f 100644 --- a/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shop_settings/ShopBasicSettingsViewModelTest.kt +++ b/app/src/test/kotlin/com/nativeapptemplate/nativeapptemplatefree/ui/shop_settings/ShopBasicSettingsViewModelTest.kt @@ -74,16 +74,20 @@ class ShopBasicSettingsViewModelTest { fun stateIsUpdated_whenUpdatingShop_becomesTrue() = runTest { backgroundScope.launch(UnconfinedTestDispatcher()) { viewModel.uiState.collect() } - viewModel.updateName(testInputShop.getName()) - viewModel.updateDescription(testInputShop.getDescription()) - viewModel.updateTimeZone(testInputShop.getTimeZone()) + shopRepository.sendShop(testInputShop) + + viewModel.reload() + + viewModel.updateName(testInputNewName) + viewModel.updateDescription(testInputNewDescription) + viewModel.updateTimeZone(testInputNewTimeZone) val uiStateValue = viewModel.uiState.value - assertEquals(uiStateValue.name, testInputShop.getName()) - assertEquals(uiStateValue.description, testInputShop.getDescription()) - assertEquals(uiStateValue.timeZone, testInputShop.getTimeZone()) + assertEquals(uiStateValue.name, testInputNewName) + assertEquals(uiStateValue.description, testInputNewDescription) + assertEquals(uiStateValue.timeZone, testInputNewTimeZone) + assertFalse(viewModel.hasInvalidData()) - shopRepository.sendShop(testInputShop) viewModel.updateShop() assertTrue(viewModel.uiState.value.isUpdated) @@ -133,3 +137,6 @@ private val testInputShopsData = private val testInputShop = Shop( datum = testInputShopsData, ) +private const val testInputNewName = "Olivia Clark" +private const val testInputNewDescription = "olivia@example.com" +private const val testInputNewTimeZone = "Hawaii"