Add home screen
This commit is contained in:
@@ -14,8 +14,8 @@
|
||||
<string name="auth_error_unknown">Coś poszło nie tak. Spróbuj ponownie.</string>
|
||||
|
||||
<!-- Phase 2.1 — App shell navigation tab labels (UI-03, CONTEXT D-03) -->
|
||||
<string name="shell_tab_home">Start</string>
|
||||
<string name="shell_tab_planner">Planer</string>
|
||||
<string name="shell_tab_recipes">Przepisy</string>
|
||||
<string name="shell_tab_pantry">Spiżarnia</string>
|
||||
<string name="shell_tab_shopping">Zakupy</string>
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
<string name="dock_expand_a11y">Rozwiń pasek nawigacji</string>
|
||||
|
||||
<!-- Phase 2.1 — Empty-state copy (UI-09, CONTEXT D-10/D-11/D-12) -->
|
||||
<string name="empty_home_title">Tu pojawi się Twój dzień</string>
|
||||
<string name="empty_home_subtitle">Wkrótce zobaczysz tu podsumowania i propozycje.</string>
|
||||
<string name="empty_planner_title">Twój plan tygodnia czeka</string>
|
||||
<string name="empty_planner_subtitle">Wkrótce zobaczysz tu zaplanowane posiłki.</string>
|
||||
<string name="empty_recipes_title">Tu pojawi się Twoja książka kucharska</string>
|
||||
<string name="empty_recipes_subtitle">Po dodaniu pierwszych przepisów zobaczysz je w tym miejscu.</string>
|
||||
<string name="empty_pantry_title">Spiżarnia jest jeszcze pusta</string>
|
||||
<string name="empty_pantry_subtitle">Wkrótce zobaczysz tu wszystko, co masz pod ręką.</string>
|
||||
<string name="empty_shopping_title">Lista zakupów czeka na Twój plan</string>
|
||||
|
||||
@@ -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<HomeViewModel>()
|
||||
viewModel<PlannerViewModel>()
|
||||
viewModel<RecipesViewModel>()
|
||||
viewModel<PantryViewModel>()
|
||||
viewModel<ShoppingViewModel>()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Screen.Home.Root> {
|
||||
val vm: HomeViewModel = koinViewModel()
|
||||
HomeScreen(viewModel = vm)
|
||||
}
|
||||
entry<Screen.Planner.Home> {
|
||||
val vm: PlannerViewModel = koinViewModel()
|
||||
PlannerScreen(viewModel = vm)
|
||||
}
|
||||
entry<Screen.Recipes.Home> {
|
||||
val vm: RecipesViewModel = koinViewModel()
|
||||
RecipesScreen(viewModel = vm)
|
||||
}
|
||||
entry<Screen.Pantry.Home> {
|
||||
val vm: PantryViewModel = koinViewModel()
|
||||
PantryScreen(viewModel = vm)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<HomeState> = _state.asStateFlow()
|
||||
}
|
||||
@@ -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<RecipesState> = _state.asStateFlow()
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user