From 8eda4b04ee2b523e6d43e3293c0d3d2d145c7bd8 Mon Sep 17 00:00:00 2001 From: ulfrxdev Date: Sun, 17 May 2026 20:44:25 +0200 Subject: [PATCH] Add home screen --- .../composeResources/values/strings.xml | 6 ++--- .../kotlin/dev/ulfrx/recipe/di/ShellModule.kt | 4 +-- .../recipe/navigation/DockDestination.kt | 16 ++++++------ .../ulfrx/recipe/navigation/RootNavDisplay.kt | 12 ++++----- .../dev/ulfrx/recipe/navigation/Screen.kt | 21 +++++++++------- .../RecipesScreen.kt => home/HomeScreen.kt} | 25 +++++++------------ .../recipe/ui/screens/home/HomeViewModel.kt | 15 +++++++++++ .../ui/screens/recipes/RecipesViewModel.kt | 19 -------------- .../dev/ulfrx/recipe/ui/theme/RecipeGlass.kt | 2 +- 9 files changed, 56 insertions(+), 64 deletions(-) rename composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/{recipes/RecipesScreen.kt => home/HomeScreen.kt} (68%) create mode 100644 composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeViewModel.kt delete mode 100644 composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesViewModel.kt diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 12b56c0..94dc2ad 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -14,8 +14,8 @@ Coś poszło nie tak. Spróbuj ponownie. + Start Planer - Przepisy Spiżarnia Zakupy @@ -37,10 +37,10 @@ Rozwiń pasek nawigacji + Tu pojawi się Twój dzień + Wkrótce zobaczysz tu podsumowania i propozycje. Twój plan tygodnia czeka Wkrótce zobaczysz tu zaplanowane posiłki. - Tu pojawi się Twoja książka kucharska - Po dodaniu pierwszych przepisów zobaczysz je w tym miejscu. Spiżarnia jest jeszcze pusta Wkrótce zobaczysz tu wszystko, co masz pod ręką. Lista zakupów czeka na Twój plan diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/ShellModule.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/ShellModule.kt index 1739354..b73a712 100644 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/ShellModule.kt +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/ShellModule.kt @@ -1,8 +1,8 @@ package dev.ulfrx.recipe.di +import dev.ulfrx.recipe.ui.screens.home.HomeViewModel import dev.ulfrx.recipe.ui.screens.pantry.PantryViewModel import dev.ulfrx.recipe.ui.screens.planner.PlannerViewModel -import dev.ulfrx.recipe.ui.screens.recipes.RecipesViewModel import dev.ulfrx.recipe.ui.screens.search.ShellSearchViewModel import dev.ulfrx.recipe.ui.screens.shopping.ShoppingViewModel import org.koin.dsl.module @@ -14,8 +14,8 @@ val shellModule = // Active-tab tracking lives in TabNavigator (a `remember`-scoped state holder // owned by AppShell), not in a shell-level VM, so there is no ShellViewModel // to register. + viewModel() viewModel() - viewModel() viewModel() viewModel() diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/DockDestination.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/DockDestination.kt index 70a9f29..95d1476 100644 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/DockDestination.kt +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/DockDestination.kt @@ -1,16 +1,16 @@ package dev.ulfrx.recipe.navigation import androidx.compose.ui.graphics.vector.ImageVector -import com.composables.icons.lucide.BookOpenText import com.composables.icons.lucide.CalendarDays +import com.composables.icons.lucide.House import com.composables.icons.lucide.Lucide import com.composables.icons.lucide.Package import com.composables.icons.lucide.ShoppingCart import org.jetbrains.compose.resources.StringResource import recipe.composeapp.generated.resources.Res +import recipe.composeapp.generated.resources.shell_tab_home import recipe.composeapp.generated.resources.shell_tab_pantry import recipe.composeapp.generated.resources.shell_tab_planner -import recipe.composeapp.generated.resources.shell_tab_recipes import recipe.composeapp.generated.resources.shell_tab_shopping enum class DockDestination( @@ -18,16 +18,16 @@ enum class DockDestination( val labelRes: StringResource, val icon: ImageVector, ) { + Home( + startDestination = Screen.Home.Root, + labelRes = Res.string.shell_tab_home, + icon = Lucide.House, + ), Planner( startDestination = Screen.Planner.Home, labelRes = Res.string.shell_tab_planner, icon = Lucide.CalendarDays, ), - Recipes( - startDestination = Screen.Recipes.Home, - labelRes = Res.string.shell_tab_recipes, - icon = Lucide.BookOpenText, - ), Pantry( startDestination = Screen.Pantry.Home, labelRes = Res.string.shell_tab_pantry, @@ -41,6 +41,6 @@ enum class DockDestination( ; companion object { - val Default: DockDestination = Planner + val Default: DockDestination = Home } } diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/RootNavDisplay.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/RootNavDisplay.kt index f210271..67e4c60 100644 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/RootNavDisplay.kt +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/RootNavDisplay.kt @@ -10,12 +10,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation3.runtime.entryProvider import androidx.navigation3.ui.NavDisplay +import dev.ulfrx.recipe.ui.screens.home.HomeScreen +import dev.ulfrx.recipe.ui.screens.home.HomeViewModel import dev.ulfrx.recipe.ui.screens.pantry.PantryScreen import dev.ulfrx.recipe.ui.screens.pantry.PantryViewModel import dev.ulfrx.recipe.ui.screens.planner.PlannerScreen import dev.ulfrx.recipe.ui.screens.planner.PlannerViewModel -import dev.ulfrx.recipe.ui.screens.recipes.RecipesScreen -import dev.ulfrx.recipe.ui.screens.recipes.RecipesViewModel import dev.ulfrx.recipe.ui.screens.shopping.ShoppingScreen import dev.ulfrx.recipe.ui.screens.shopping.ShoppingViewModel import org.koin.compose.viewmodel.koinViewModel @@ -72,14 +72,14 @@ fun RootNavDisplay( modifier = Modifier.fillMaxSize(), onBack = { navigator.goBack(tab) }, entryProvider = entryProvider { + entry { + val vm: HomeViewModel = koinViewModel() + HomeScreen(viewModel = vm) + } entry { val vm: PlannerViewModel = koinViewModel() PlannerScreen(viewModel = vm) } - entry { - val vm: RecipesViewModel = koinViewModel() - RecipesScreen(viewModel = vm) - } entry { val vm: PantryViewModel = koinViewModel() PantryScreen(viewModel = vm) diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/Screen.kt index 0e9074f..3ce8c11 100644 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/Screen.kt @@ -7,22 +7,25 @@ import kotlinx.serialization.Serializable * Type-safe Nav 3 destinations. Each leaf is a `@Serializable` `NavKey` so the * back stack can be persisted (Nav 3 uses kotlinx-serialization for restoration). * - * Screens are grouped by tab so future detail destinations (Phase 5+) slot in - * without polluting the top-level namespace — e.g. `Screen.Recipes.Detail(id)`. - * The grouping is purely a code-organisation convenience; Nav 3 treats each - * leaf as an independent NavKey regardless of nesting. + * Screens are grouped by tab so future detail destinations slot in without + * polluting the top-level namespace — e.g. `Screen.Pantry.Detail(id)`. The + * grouping is purely a code-organisation convenience; Nav 3 treats each leaf as + * an independent NavKey regardless of nesting. + * + * The Recipes catalog has no own tab — it is reached via the shell-wide search + * destination (see `ShellSearchViewModel`). */ sealed interface Screen : NavKey { + sealed interface Home : Screen { + @Serializable + data object Root : Home + } + sealed interface Planner : Screen { @Serializable data object Home : Planner } - sealed interface Recipes : Screen { - @Serializable - data object Home : Recipes - } - sealed interface Pantry : Screen { @Serializable data object Home : Pantry diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesScreen.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeScreen.kt similarity index 68% rename from composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesScreen.kt rename to composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeScreen.kt index bd6a246..ddf3f4c 100644 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeScreen.kt @@ -1,4 +1,4 @@ -package dev.ulfrx.recipe.ui.screens.recipes +package dev.ulfrx.recipe.ui.screens.home import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -19,19 +19,12 @@ import dev.ulfrx.recipe.ui.components.empty.EmptyState import dev.ulfrx.recipe.ui.theme.RecipeTheme import org.jetbrains.compose.resources.stringResource import recipe.composeapp.generated.resources.Res -import recipe.composeapp.generated.resources.empty_recipes_subtitle -import recipe.composeapp.generated.resources.empty_recipes_title -import recipe.composeapp.generated.resources.shell_tab_recipes +import recipe.composeapp.generated.resources.empty_home_subtitle +import recipe.composeapp.generated.resources.empty_home_title +import recipe.composeapp.generated.resources.shell_tab_home -/** - * Phase 2.1 — empty-state screen for the Recipes tab. Phase 5 replaces the - * empty body with the recipe catalog grid. - * - * Search is now shell-wide (see `AppShell` + `ShellSearchViewModel`) — this - * screen no longer owns any bottom-chrome state. - */ @Composable -fun RecipesScreen(viewModel: RecipesViewModel) { +fun HomeScreen(viewModel: HomeViewModel) { @Suppress("UNUSED_VARIABLE") val state by viewModel.state.collectAsStateWithLifecycle() @@ -47,15 +40,15 @@ fun RecipesScreen(viewModel: RecipesViewModel) { verticalArrangement = Arrangement.Top, ) { BasicText( - text = stringResource(Res.string.shell_tab_recipes), + text = stringResource(Res.string.shell_tab_home), style = RecipeTheme.typography.title.copy(color = RecipeTheme.colors.content), modifier = Modifier.padding(horizontal = RecipeTheme.spacing.lg), ) Box(modifier = Modifier.fillMaxSize()) { EmptyState( - icon = DockDestination.Recipes.icon, - title = stringResource(Res.string.empty_recipes_title), - subtitle = stringResource(Res.string.empty_recipes_subtitle), + icon = DockDestination.Home.icon, + title = stringResource(Res.string.empty_home_title), + subtitle = stringResource(Res.string.empty_home_subtitle), ) } } diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeViewModel.kt new file mode 100644 index 0000000..712631b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/home/HomeViewModel.kt @@ -0,0 +1,15 @@ +package dev.ulfrx.recipe.ui.screens.home + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +data class HomeState( + val isEmpty: Boolean = true, +) + +class HomeViewModel : ViewModel() { + private val _state = MutableStateFlow(HomeState()) + val state: StateFlow = _state.asStateFlow() +} diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesViewModel.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesViewModel.kt deleted file mode 100644 index 488d667..0000000 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesViewModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dev.ulfrx.recipe.ui.screens.recipes - -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** - * UI state for [RecipesScreen]. Phase 2.1 ships only the empty state. Phase 5 - * (Recipe Catalog Read Path) extends this with `recipes` etc. - */ -data class RecipesState( - val isEmpty: Boolean = true, -) - -class RecipesViewModel : ViewModel() { - private val _state = MutableStateFlow(RecipesState()) - val state: StateFlow = _state.asStateFlow() -} diff --git a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/theme/RecipeGlass.kt b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/theme/RecipeGlass.kt index a9017c1..c52f66e 100644 --- a/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/theme/RecipeGlass.kt +++ b/composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/theme/RecipeGlass.kt @@ -7,7 +7,7 @@ data object RecipeGlass { val menu: RecipeGlassStyle = RecipeGlassStyle( refraction = 0.10f, curve = 0.5f, - edge = 0.05f, + edge = 0.04f, dispersion = 0.05f, saturation = 0.5f, contrast = 1.3f,