feat(enrichment): apply semantic markup
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens
|
||||
// [FILE] MainActivity.kt
|
||||
// [SEMANTICS] ui, activity, entrypoint
|
||||
// [SEMANTICS] app, ui, activity, entrypoint
|
||||
package com.homebox.lens
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -22,7 +22,7 @@ import timber.log.Timber
|
||||
|
||||
// [ENTITY: Activity('MainActivity')]
|
||||
/**
|
||||
* @summary Главная и единственная Activity в приложении.
|
||||
* @summary The main and only Activity in the application.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens
|
||||
// [FILE] MainApplication.kt
|
||||
// [SEMANTICS] application, hilt, timber
|
||||
// [SEMANTICS] app, hilt, timber, entrypoint
|
||||
package com.homebox.lens
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -11,7 +11,7 @@ import timber.log.Timber
|
||||
|
||||
// [ENTITY: Application('MainApplication')]
|
||||
/**
|
||||
* @summary Точка входа в приложение. Инициализирует Hilt и Timber.
|
||||
* @summary The entry point of the application. Initializes Hilt and Timber.
|
||||
*/
|
||||
@HiltAndroidApp
|
||||
class MainApplication : Application() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.navigation
|
||||
// [FILE] NavGraph.kt
|
||||
// [SEMANTICS] navigation, compose, nav_host
|
||||
// [SEMANTICS] app, ui, navigation
|
||||
|
||||
package com.homebox.lens.navigation
|
||||
|
||||
@@ -34,11 +34,11 @@ import com.homebox.lens.ui.screen.splash.SplashScreen
|
||||
// [RELATION: Function('NavGraph')] -> [CREATES_INSTANCE_OF] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('NavGraph')] -> [USES] -> [Screen('SplashScreen')]
|
||||
/**
|
||||
* @summary Определяет граф навигации для всего приложения с использованием Jetpack Compose Navigation.
|
||||
* @param navController Контроллер навигации.
|
||||
* @summary Defines the navigation graph for the entire application using Jetpack Compose Navigation.
|
||||
* @param navController The navigation controller.
|
||||
* @see Screen
|
||||
* @sideeffect Регистрирует все экраны и управляет состоянием навигации.
|
||||
* @invariant Стартовый экран - `Screen.Setup`.
|
||||
* @sideeffect Registers all screens and manages the navigation state.
|
||||
* @invariant The start screen is `Screen.Splash`.
|
||||
*/
|
||||
@Composable
|
||||
fun NavGraph(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.navigation
|
||||
// [FILE] NavigationActions.kt
|
||||
// [SEMANTICS] navigation, controller, actions
|
||||
// [SEMANTICS] app, ui, navigation, actions
|
||||
package com.homebox.lens.navigation
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -11,16 +11,16 @@ import timber.log.Timber
|
||||
// [ENTITY: Class('NavigationActions')]
|
||||
// [RELATION: Class('NavigationActions')] -> [DEPENDS_ON] -> [Framework('NavHostController')]
|
||||
/**
|
||||
* @summary Класс-обертка над NavHostController для предоставления типизированных навигационных действий.
|
||||
* @param navController Контроллер Jetpack Navigation.
|
||||
* @invariant Все навигационные действия должны использовать предоставленный navController.
|
||||
* @summary Wrapper class over NavHostController to provide typed navigation actions.
|
||||
* @param navController The Jetpack Navigation controller.
|
||||
* @invariant All navigation actions must use the provided navController.
|
||||
*/
|
||||
class NavigationActions(private val navController: NavHostController) {
|
||||
|
||||
// [ENTITY: Function('navigateToDashboard')]
|
||||
/**
|
||||
* @summary Навигация на главный экран.
|
||||
* @sideeffect Очищает back stack до главного экрана, чтобы избежать циклов.
|
||||
* @summary Navigation to the main screen.
|
||||
* @sideeffect Clears the back stack up to the main screen to avoid cycles.
|
||||
*/
|
||||
fun navigateToDashboard() {
|
||||
Timber.i("[INFO][ACTION][navigate_to_dashboard] Navigating to Dashboard.")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// [PACKAGE] com.homebox.lens.navigation
|
||||
// [FILE] Screen.kt
|
||||
// [SEMANTICS] navigation, routes, sealed_class
|
||||
// [SEMANTICS] app, ui, navigation, routes
|
||||
package com.homebox.lens.navigation
|
||||
|
||||
// [ENTITY: SealedClass('Screen')]
|
||||
/**
|
||||
* @summary Запечатанный класс для определения маршрутов навигации в приложении.
|
||||
* @description Обеспечивает типобезопасность при навигации.
|
||||
* @param route Строковый идентификатор маршрута.
|
||||
* @summary Sealed class for defining navigation routes in the application.
|
||||
* @description Provides type safety during navigation.
|
||||
* @param route The string identifier of the route.
|
||||
*/
|
||||
sealed class Screen(val route: String) {
|
||||
// [ENTITY: Object('Splash')]
|
||||
@@ -26,11 +26,11 @@ sealed class Screen(val route: String) {
|
||||
data object InventoryList : Screen("inventory_list_screen") {
|
||||
// [ENTITY: Function('withFilter')]
|
||||
/**
|
||||
* @summary Создает маршрут для экрана списка инвентаря с параметром фильтра.
|
||||
* @param key Ключ фильтра (например, "label" или "location").
|
||||
* @param value Значение фильтра (например, ID метки или местоположения).
|
||||
* @return Строку полного маршрута с query-параметром.
|
||||
* @throws IllegalArgumentException если ключ или значение пустые.
|
||||
* @summary Creates a route for the inventory list screen with a filter parameter.
|
||||
* @param key The filter key (e.g., "label" or "location").
|
||||
* @param value The filter value (e.g., the ID of the label or location).
|
||||
* @return A string of the full route with a query parameter.
|
||||
* @throws IllegalArgumentException if the key or value is blank.
|
||||
*/
|
||||
fun withFilter(key: String, value: String): String {
|
||||
require(key.isNotBlank()) { "Filter key cannot be blank." }
|
||||
@@ -47,10 +47,10 @@ sealed class Screen(val route: String) {
|
||||
data object ItemDetails : Screen("item_details_screen/{itemId}") {
|
||||
// [ENTITY: Function('createRoute')]
|
||||
/**
|
||||
* @summary Создает маршрут для экрана деталей элемента с указанным ID.
|
||||
* @param itemId ID элемента для отображения.
|
||||
* @return Строку полного маршрута.
|
||||
* @throws IllegalArgumentException если itemId пустой.
|
||||
* @summary Creates a route for the item details screen with the specified ID.
|
||||
* @param itemId The ID of the item to display.
|
||||
* @return A string of the full route.
|
||||
* @throws IllegalArgumentException if itemId is blank.
|
||||
*/
|
||||
fun createRoute(itemId: String): String {
|
||||
require(itemId.isNotBlank()) { "itemId не может быть пустым." }
|
||||
@@ -66,9 +66,9 @@ sealed class Screen(val route: String) {
|
||||
data object ItemEdit : Screen("item_edit_screen?itemId={itemId}") {
|
||||
// [ENTITY: Function('createRoute')]
|
||||
/**
|
||||
* @summary Создает маршрут для экрана редактирования элемента с указанным ID.
|
||||
* @param itemId ID элемента для редактирования. Null, если создается новый элемент.
|
||||
* @return Строку полного маршрута.
|
||||
* @summary Creates a route for the item edit screen with the specified ID.
|
||||
* @param itemId The ID of the item to edit. Null if a new item is being created.
|
||||
* @return A string of the full route.
|
||||
*/
|
||||
fun createRoute(itemId: String? = null): String {
|
||||
return itemId?.let { "item_edit_screen?itemId=$it" } ?: "item_edit_screen"
|
||||
@@ -85,9 +85,9 @@ sealed class Screen(val route: String) {
|
||||
data object LabelEdit : Screen("label_edit_screen?labelId={labelId}") {
|
||||
// [ENTITY: Function('createRoute')]
|
||||
/**
|
||||
* @summary Создает маршрут для экрана редактирования метки с указанным ID.
|
||||
* @param labelId ID метки для редактирования. Null, если создается новая метка.
|
||||
* @return Строку полного маршрута.
|
||||
* @summary Creates a route for the label edit screen with the specified ID.
|
||||
* @param labelId The ID of the label to edit. Null if a new label is being created.
|
||||
* @return A string of the full route.
|
||||
*/
|
||||
fun createRoute(labelId: String? = null): String {
|
||||
return labelId?.let { "label_edit_screen?labelId=$it" } ?: "label_edit_screen"
|
||||
@@ -104,10 +104,10 @@ sealed class Screen(val route: String) {
|
||||
data object LocationEdit : Screen("location_edit_screen/{locationId}") {
|
||||
// [ENTITY: Function('createRoute')]
|
||||
/**
|
||||
* @summary Создает маршрут для экрана редактирования местоположения с указанным ID.
|
||||
* @param locationId ID местоположения для редактирования.
|
||||
* @return Строку полного маршрута.
|
||||
* @throws IllegalArgumentException если locationId пустой.
|
||||
* @summary Creates a route for the location edit screen with the specified ID.
|
||||
* @param locationId The ID of the location to edit.
|
||||
* @return A string of the full route.
|
||||
* @throws IllegalArgumentException if locationId is blank.
|
||||
*/
|
||||
fun createRoute(locationId: String): String {
|
||||
require(locationId.isNotBlank()) { "locationId не может быть пустым." }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.common
|
||||
// [FILE] AppDrawer.kt
|
||||
// [SEMANTICS] ui, common, navigation_drawer
|
||||
// [SEMANTICS] app, ui, common, navigation
|
||||
package com.homebox.lens.ui.common
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -30,10 +30,10 @@ import com.homebox.lens.navigation.Screen
|
||||
// [ENTITY: Function('AppDrawerContent')]
|
||||
// [RELATION: Function('AppDrawerContent')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
/**
|
||||
* @summary Контент для бокового навигационного меню (Drawer).
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @param onCloseDrawer Лямбда для закрытия бокового меню.
|
||||
* @summary Content for the side navigation menu (Drawer).
|
||||
* @param currentRoute The current route to highlight the active item.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
* @param onCloseDrawer Lambda to close the side menu.
|
||||
*/
|
||||
@Composable
|
||||
internal fun AppDrawerContent(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.common
|
||||
// [FILE] MainScaffold.kt
|
||||
// [SEMANTICS] ui, common, scaffold, navigation_drawer
|
||||
// [SEMANTICS] app, ui, common, scaffold
|
||||
|
||||
package com.homebox.lens.ui.common
|
||||
|
||||
@@ -21,14 +21,14 @@ import kotlinx.coroutines.launch
|
||||
// [RELATION: Function('MainScaffold')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('MainScaffold')] -> [CALLS] -> [Function('AppDrawerContent')]
|
||||
/**
|
||||
* @summary Общая обертка для экранов, включающая Scaffold и Navigation Drawer.
|
||||
* @param topBarTitle Заголовок для TopAppBar.
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @param topBarActions Composable-функция для отображения действий (иконок) в TopAppBar.
|
||||
* @param content Основное содержимое экрана, которое будет отображено внутри Scaffold.
|
||||
* @sideeffect Управляет состоянием (открыто/закрыто) бокового меню (ModalNavigationDrawer).
|
||||
* @invariant TopAppBar всегда отображается с иконкой меню.
|
||||
* @summary A common wrapper for screens that includes a Scaffold and Navigation Drawer.
|
||||
* @param topBarTitle The title for the TopAppBar.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
* @param topBarActions A Composable function to display actions (icons) in the TopAppBar.
|
||||
* @param content The main content of the screen to be displayed inside the Scaffold.
|
||||
* @sideeffect Manages the state (open/closed) of the side menu (ModalNavigationDrawer).
|
||||
* @invariant The TopAppBar is always displayed with a menu icon.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.components
|
||||
// [FILE] ColorPicker.kt
|
||||
// [SEMANTICS] ui, component, color_selection
|
||||
// [SEMANTICS] app, ui, component, color
|
||||
|
||||
package com.homebox.lens.ui.components
|
||||
|
||||
@@ -25,10 +25,10 @@ import com.homebox.lens.R
|
||||
|
||||
// [ENTITY: Function('ColorPicker')]
|
||||
/**
|
||||
* @summary Компонент для выбора цвета.
|
||||
* @param selectedColor Текущий выбранный цвет в формате HEX строки (например, "#FFFFFF").
|
||||
* @param onColorSelected Лямбда-функция, вызываемая при выборе нового цвета.
|
||||
* @param modifier Модификатор для настройки внешнего вида.
|
||||
* @summary A component for color selection.
|
||||
* @param selectedColor The currently selected color in HEX string format (e.g., "#FFFFFF").
|
||||
* @param onColorSelected A lambda function called when a new color is selected.
|
||||
* @param modifier A modifier for customizing the appearance.
|
||||
*/
|
||||
@Composable
|
||||
fun ColorPicker(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.components
|
||||
// [FILE] LoadingOverlay.kt
|
||||
// [SEMANTICS] ui, component, loading
|
||||
// [SEMANTICS] app, ui, component, loading
|
||||
|
||||
package com.homebox.lens.ui.components
|
||||
|
||||
@@ -18,7 +18,7 @@ import androidx.compose.ui.graphics.Color
|
||||
|
||||
// [ENTITY: Function('LoadingOverlay')]
|
||||
/**
|
||||
* @summary Полноэкранный оверлей с индикатором загрузки.
|
||||
* @summary A full-screen overlay with a loading indicator.
|
||||
*/
|
||||
@Composable
|
||||
fun LoadingOverlay() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.mapper
|
||||
// [FILE] ItemMapper.kt
|
||||
// [SEMANTICS] ui, mapper, item
|
||||
// [SEMANTICS] app, ui, mapper, item
|
||||
package com.homebox.lens.ui.mapper
|
||||
|
||||
import com.homebox.lens.domain.model.Item
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.dashboard
|
||||
// [FILE] DashboardScreen.kt
|
||||
// [SEMANTICS] ui, screen, dashboard, compose, navigation
|
||||
// [SEMANTICS] app, ui, screen, dashboard
|
||||
package com.homebox.lens.ui.screen.dashboard
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -37,11 +37,11 @@ import timber.log.Timber
|
||||
// [RELATION: Function('DashboardScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('DashboardScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
/**
|
||||
* @summary Главная Composable-функция для экрана "Панель управления".
|
||||
* @param viewModel ViewModel для этого экрана, предоставляется через Hilt.
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @sideeffect Вызывает навигационные лямбды при взаимодействии с UI.
|
||||
* @summary The main Composable function for the "Dashboard" screen.
|
||||
* @param viewModel The ViewModel for this screen, provided by Hilt.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
* @sideeffect Calls navigation lambdas upon UI interaction.
|
||||
*/
|
||||
@Composable
|
||||
fun DashboardScreen(
|
||||
@@ -82,11 +82,11 @@ fun DashboardScreen(
|
||||
// [ENTITY: Function('DashboardContent')]
|
||||
// [RELATION: Function('DashboardContent')] -> [CONSUMES_STATE] -> [SealedInterface('DashboardUiState')]
|
||||
/**
|
||||
* @summary Отображает основной контент экрана в зависимости от uiState.
|
||||
* @param modifier Модификатор для стилизации.
|
||||
* @param uiState Текущее состояние UI экрана.
|
||||
* @param onLocationClick Лямбда-обработчик нажатия на местоположение.
|
||||
* @param onLabelClick Лямбда-обработчик нажатия на метку.
|
||||
* @summary Displays the main content of the screen depending on the uiState.
|
||||
* @param modifier A modifier for styling.
|
||||
* @param uiState The current UI state of the screen.
|
||||
* @param onLocationClick A lambda handler for clicking on a location.
|
||||
* @param onLabelClick A lambda handler for clicking on a label.
|
||||
*/
|
||||
@Composable
|
||||
private fun DashboardContent(
|
||||
@@ -132,8 +132,8 @@ private fun DashboardContent(
|
||||
// [ENTITY: Function('StatisticsSection')]
|
||||
// [RELATION: Function('StatisticsSection')] -> [DEPENDS_ON] -> [DataClass('GroupStatistics')]
|
||||
/**
|
||||
* @summary Секция для отображения общей статистики.
|
||||
* @param statistics Объект со статистическими данными.
|
||||
* @summary Section for displaying general statistics.
|
||||
* @param statistics The object with statistical data.
|
||||
*/
|
||||
@Composable
|
||||
private fun StatisticsSection(statistics: GroupStatistics) {
|
||||
@@ -164,9 +164,9 @@ private fun StatisticsSection(statistics: GroupStatistics) {
|
||||
|
||||
// [ENTITY: Function('StatisticCard')]
|
||||
/**
|
||||
* @summary Карточка для отображения одного статистического показателя.
|
||||
* @param title Название показателя.
|
||||
* @param value Значение показателя.
|
||||
* @summary Card for displaying a single statistical indicator.
|
||||
* @param title The name of the indicator.
|
||||
* @param value The value of the indicator.
|
||||
*/
|
||||
@Composable
|
||||
private fun StatisticCard(title: String, value: String) {
|
||||
@@ -180,8 +180,8 @@ private fun StatisticCard(title: String, value: String) {
|
||||
// [ENTITY: Function('RecentlyAddedSection')]
|
||||
// [RELATION: Function('RecentlyAddedSection')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
|
||||
/**
|
||||
* @summary Секция для отображения недавно добавленных элементов.
|
||||
* @param items Список элементов для отображения.
|
||||
* @summary Section for displaying recently added items.
|
||||
* @param items The list of items to display.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentlyAddedSection(items: List<ItemSummary>) {
|
||||
@@ -213,8 +213,8 @@ private fun RecentlyAddedSection(items: List<ItemSummary>) {
|
||||
// [ENTITY: Function('ItemCard')]
|
||||
// [RELATION: Function('ItemCard')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
|
||||
/**
|
||||
* @summary Карточка для отображения краткой информации об элементе.
|
||||
* @param item Элемент для отображения.
|
||||
* @summary Card for displaying brief information about an item.
|
||||
* @param item The item to display.
|
||||
*/
|
||||
@Composable
|
||||
private fun ItemCard(item: ItemSummary) {
|
||||
@@ -236,9 +236,9 @@ private fun ItemCard(item: ItemSummary) {
|
||||
// [ENTITY: Function('LocationsSection')]
|
||||
// [RELATION: Function('LocationsSection')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
|
||||
/**
|
||||
* @summary Секция для отображения местоположений в виде чипсов.
|
||||
* @param locations Список местоположений.
|
||||
* @param onLocationClick Лямбда-обработчик нажатия на местоположение.
|
||||
* @summary Section for displaying locations as chips.
|
||||
* @param locations The list of locations.
|
||||
* @param onLocationClick A lambda handler for clicking on a location.
|
||||
*/
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
@@ -265,9 +265,9 @@ private fun LocationsSection(locations: List<LocationOutCount>, onLocationClick:
|
||||
// [ENTITY: Function('LabelsSection')]
|
||||
// [RELATION: Function('LabelsSection')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
|
||||
/**
|
||||
* @summary Секция для отображения меток в виде чипсов.
|
||||
* @param labels Список меток.
|
||||
* @param onLabelClick Лямбда-обработчик нажатия на метку.
|
||||
* @summary Section for displaying labels as chips.
|
||||
* @param labels The list of labels.
|
||||
* @param onLabelClick A lambda handler for clicking on a label.
|
||||
*/
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.dashboard
|
||||
// [FILE] DashboardUiState.kt
|
||||
// [SEMANTICS] ui, state, dashboard
|
||||
// [SEMANTICS] app, ui, state, dashboard
|
||||
package com.homebox.lens.ui.screen.dashboard
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -12,8 +12,8 @@ import com.homebox.lens.domain.model.LocationOutCount
|
||||
|
||||
// [ENTITY: SealedInterface('DashboardUiState')]
|
||||
/**
|
||||
* @summary Определяет все возможные состояния для экрана "Дэшборд".
|
||||
* @invariant В любой момент времени экран может находиться только в одном из этих состояний.
|
||||
* @summary Defines all possible states for the "Dashboard" screen.
|
||||
* @invariant At any given time, the screen can only be in one of these states.
|
||||
*/
|
||||
sealed interface DashboardUiState {
|
||||
// [ENTITY: DataClass('Success')]
|
||||
@@ -22,11 +22,11 @@ sealed interface DashboardUiState {
|
||||
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
|
||||
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
|
||||
/**
|
||||
* @summary Состояние успешной загрузки данных.
|
||||
* @param statistics Статистика по инвентарю.
|
||||
* @param locations Список локаций со счетчиками.
|
||||
* @param labels Список всех меток.
|
||||
* @param recentlyAddedItems Список недавно добавленных товаров.
|
||||
* @summary The state of a successful data load.
|
||||
* @param statistics The inventory statistics.
|
||||
* @param locations The list of locations with counters.
|
||||
* @param labels The list of all labels.
|
||||
* @param recentlyAddedItems The list of recently added items.
|
||||
*/
|
||||
data class Success(
|
||||
val statistics: GroupStatistics,
|
||||
@@ -38,15 +38,15 @@ sealed interface DashboardUiState {
|
||||
|
||||
// [ENTITY: DataClass('Error')]
|
||||
/**
|
||||
* @summary Состояние ошибки во время загрузки данных.
|
||||
* @param message Человекочитаемое сообщение об ошибке.
|
||||
* @summary The state of an error during data loading.
|
||||
* @param message A human-readable error message.
|
||||
*/
|
||||
data class Error(val message: String) : DashboardUiState
|
||||
// [END_ENTITY: DataClass('Error')]
|
||||
|
||||
// [ENTITY: Object('Loading')]
|
||||
/**
|
||||
* @summary Состояние, когда данные для экрана загружаются.
|
||||
* @summary The state when data for the screen is being loaded.
|
||||
*/
|
||||
data object Loading : DashboardUiState
|
||||
// [END_ENTITY: Object('Loading')]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.dashboard
|
||||
// [FILE] DashboardViewModel.kt
|
||||
// [SEMANTICS] ui_logic, dashboard, state_management, sealed_state, parallel_data_loading, timber_logging
|
||||
// [SEMANTICS] app, ui, viewmodel, dashboard
|
||||
package com.homebox.lens.ui.screen.dashboard
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -24,10 +24,10 @@ import javax.inject.Inject
|
||||
// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetRecentlyAddedItemsUseCase')]
|
||||
// [RELATION: ViewModel('DashboardViewModel')] -> [EMITS_STATE] -> [SealedInterface('DashboardUiState')]
|
||||
/**
|
||||
* @summary ViewModel для главного экрана (Dashboard).
|
||||
* @description Оркестрирует загрузку данных для Dashboard, используя строгую модель состояний
|
||||
* (`DashboardUiState`), и обрабатывает параллельные запросы без состояний гонки.
|
||||
* @invariant `uiState` всегда является одним из состояний, определенных в `DashboardUiState`.
|
||||
* @summary ViewModel for the main screen (Dashboard).
|
||||
* @description Orchestrates the loading of data for the Dashboard, using a strict state model
|
||||
* (`DashboardUiState`), and handles parallel requests without race conditions.
|
||||
* @invariant `uiState` is always one of the states defined in `DashboardUiState`.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class DashboardViewModel @Inject constructor(
|
||||
@@ -46,10 +46,10 @@ class DashboardViewModel @Inject constructor(
|
||||
|
||||
// [ENTITY: Function('loadDashboardData')]
|
||||
/**
|
||||
* @summary Загружает все необходимые данные для экрана Dashboard.
|
||||
* @description Выполняет UseCase'ы параллельно и обновляет UI, переключая его
|
||||
* между состояниями `Loading`, `Success` и `Error` из `DashboardUiState`.
|
||||
* @sideeffect Асинхронно обновляет `_uiState` одним из состояний `DashboardUiState`.
|
||||
* @summary Loads all necessary data for the Dashboard screen.
|
||||
* @description Executes UseCases in parallel and updates the UI by switching it
|
||||
* between the `Loading`, `Success`, and `Error` states from `DashboardUiState`.
|
||||
* @sideeffect Asynchronously updates `_uiState` with one of the `DashboardUiState` states.
|
||||
*/
|
||||
fun loadDashboardData() {
|
||||
viewModelScope.launch {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.inventorylist
|
||||
// [FILE] InventoryListScreen.kt
|
||||
// [SEMANTICS] ui, screen, inventory, list
|
||||
// [SEMANTICS] app, ui, screen, list
|
||||
|
||||
package com.homebox.lens.ui.screen.inventorylist
|
||||
|
||||
@@ -17,9 +17,9 @@ import com.homebox.lens.ui.common.MainScaffold
|
||||
// [RELATION: Function('InventoryListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('InventoryListScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Список инвентаря".
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @summary Composable function for the "Inventory List" screen.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
*/
|
||||
@Composable
|
||||
fun InventoryListScreen(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.inventorylist
|
||||
// [FILE] InventoryListViewModel.kt
|
||||
// [SEMANTICS] ui, viewmodel, inventory_list
|
||||
// [SEMANTICS] app, ui, viewmodel, list
|
||||
package com.homebox.lens.ui.screen.inventorylist
|
||||
|
||||
// [IMPORTS]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.itemdetails
|
||||
// [FILE] ItemDetailsScreen.kt
|
||||
// [SEMANTICS] ui, screen, item, details
|
||||
// [SEMANTICS] app, ui, screen, details
|
||||
|
||||
package com.homebox.lens.ui.screen.itemdetails
|
||||
|
||||
@@ -17,9 +17,9 @@ import com.homebox.lens.ui.common.MainScaffold
|
||||
// [RELATION: Function('ItemDetailsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('ItemDetailsScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Детали элемента".
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @summary Composable function for the "Item Details" screen.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
*/
|
||||
@Composable
|
||||
fun ItemDetailsScreen(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.itemdetails
|
||||
// [FILE] ItemDetailsViewModel.kt
|
||||
// [SEMANTICS] ui, viewmodel, item_details
|
||||
// [SEMANTICS] app, ui, viewmodel, details
|
||||
package com.homebox.lens.ui.screen.itemdetails
|
||||
|
||||
// [IMPORTS]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.itemedit
|
||||
// [FILE] ItemEditScreen.kt
|
||||
// [SEMANTICS] ui, screen, item, edit
|
||||
// [SEMANTICS] app, ui, screen, edit
|
||||
|
||||
package com.homebox.lens.ui.screen.itemedit
|
||||
|
||||
@@ -21,13 +21,17 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerDialog
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -38,6 +42,7 @@ import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -67,12 +72,12 @@ import java.util.Locale
|
||||
// [RELATION: Composable('ItemEditScreen')] -> [CONSUMES_STATE] -> [DataClass('ItemEditUiState')]
|
||||
// [RELATION: Composable('ItemEditScreen')] -> [CALLS] -> [Composable('MainScaffold')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Редактирование элемента".
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @param itemId ID элемента для редактирования. Null, если создается новый элемент.
|
||||
* @param viewModel ViewModel для управления состоянием экрана.
|
||||
* @param onSaveSuccess Callback, вызываемый после успешного сохранения товара.
|
||||
* @summary Composable function for the "Edit Item" screen.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
* @param itemId The ID of the item to edit. Null if a new item is being created.
|
||||
* @param viewModel The ViewModel for managing the screen's state.
|
||||
* @param onSaveSuccess A callback invoked after the item is successfully saved.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -164,34 +169,124 @@ fun ItemEditScreen(
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Location selection will require a separate component or screen.
|
||||
OutlinedTextField(
|
||||
value = item.location?.name ?: "",
|
||||
onValueChange = { /* TODO: Implement location selection */ },
|
||||
label = { Text(stringResource(R.string.item_edit_location)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { /* TODO: Implement location selection */ }) {
|
||||
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.item_edit_select_location))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Label selection will require a separate component or screen.
|
||||
OutlinedTextField(
|
||||
value = item.labels.joinToString { it.name },
|
||||
onValueChange = { /* TODO: Implement label selection */ },
|
||||
label = { Text(stringResource(R.string.item_edit_labels)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { /* TODO: Implement label selection */ }) {
|
||||
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.item_edit_select_labels))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Location Dropdown
|
||||
var locationExpanded by remember { mutableStateOf(false) }
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = locationExpanded,
|
||||
onExpandedChange = { locationExpanded = !locationExpanded }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = item.location?.name ?: "",
|
||||
onValueChange = { },
|
||||
label = { Text(stringResource(R.string.item_edit_location)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = locationExpanded)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = locationExpanded,
|
||||
onDismissRequest = { locationExpanded = false }
|
||||
) {
|
||||
uiState.allLocations.forEach { location ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(location.name) },
|
||||
onClick = {
|
||||
viewModel.updateLocation(location)
|
||||
locationExpanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Labels Dialog
|
||||
var showLabelsDialog by remember { mutableStateOf(false) }
|
||||
|
||||
OutlinedTextField(
|
||||
value = item.labels.joinToString { it.name },
|
||||
onValueChange = { },
|
||||
label = { Text(stringResource(R.string.item_edit_labels)) },
|
||||
readOnly = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { showLabelsDialog = true },
|
||||
trailingIcon = {
|
||||
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.item_edit_select_labels))
|
||||
}
|
||||
)
|
||||
|
||||
if (showLabelsDialog) {
|
||||
// This state will hold the temporary selections within the dialog
|
||||
val tempSelectedLabels = remember { mutableStateOf(item.labels.toSet()) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { showLabelsDialog = false },
|
||||
title = { Text(stringResource(R.string.item_edit_select_labels)) },
|
||||
text = {
|
||||
Column {
|
||||
uiState.allLabels.forEach { label ->
|
||||
val isChecked = tempSelectedLabels.value.contains(label)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
val currentSelection = tempSelectedLabels.value.toMutableSet()
|
||||
if (isChecked) {
|
||||
currentSelection.remove(label)
|
||||
} else {
|
||||
currentSelection.add(label)
|
||||
}
|
||||
tempSelectedLabels.value = currentSelection
|
||||
}
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
Checkbox(
|
||||
checked = isChecked,
|
||||
onCheckedChange = {
|
||||
val currentSelection = tempSelectedLabels.value.toMutableSet()
|
||||
if (it) {
|
||||
currentSelection.add(label)
|
||||
} else {
|
||||
currentSelection.remove(label)
|
||||
}
|
||||
tempSelectedLabels.value = currentSelection
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = label.name,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
// Update the ViewModel with the final selection
|
||||
viewModel.updateLabels(tempSelectedLabels.value.toList())
|
||||
showLabelsDialog = false
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.dialog_ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showLabelsDialog = false }) {
|
||||
Text(stringResource(R.string.dialog_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.itemedit
|
||||
// [FILE] ItemEditViewModel.kt
|
||||
// [SEMANTICS] ui, viewmodel, item_edit
|
||||
// [SEMANTICS] app, ui, viewmodel, edit
|
||||
|
||||
package com.homebox.lens.ui.screen.itemedit
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.homebox.lens.domain.usecase.CreateItemUseCase
|
||||
import com.homebox.lens.domain.usecase.GetAllLabelsUseCase
|
||||
import com.homebox.lens.domain.usecase.GetAllLocationsUseCase
|
||||
import com.homebox.lens.domain.usecase.GetItemDetailsUseCase
|
||||
import com.homebox.lens.data.mapper.toDomain
|
||||
import com.homebox.lens.domain.usecase.UpdateItemUseCase
|
||||
import com.homebox.lens.ui.mapper.ItemMapper
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -59,6 +60,8 @@ data class ItemEditUiState(
|
||||
* @param createItemUseCase Use case for creating a new item.
|
||||
* @param updateItemUseCase Use case for updating an existing item.
|
||||
* @param getItemDetailsUseCase Use case for fetching item details.
|
||||
* @param getAllLocationsUseCase Use case for fetching all locations.
|
||||
* @param getAllLabelsUseCase Use case for fetching all labels.
|
||||
* @param itemMapper Mapper for converting between domain and UI item models.
|
||||
*/
|
||||
@HiltViewModel
|
||||
@@ -141,7 +144,7 @@ class ItemEditViewModel @Inject constructor(
|
||||
Timber.i("[INFO][ACTION][fetching_all_locations] Fetching all locations.")
|
||||
val allLocations = getAllLocationsUseCase().map { Location(it.id, it.name) }
|
||||
Timber.i("[INFO][ACTION][fetching_all_labels] Fetching all labels.")
|
||||
val allLabels = getAllLabelsUseCase().map { Label(it.id, it.name) }
|
||||
val allLabels = getAllLabelsUseCase().map { it.toDomain() }
|
||||
_uiState.value = _uiState.value.copy(allLocations = allLocations, allLabels = allLabels)
|
||||
Timber.i("[INFO][ACTION][all_locations_labels_fetched] Successfully fetched all locations and labels.")
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.labeledit
|
||||
// [FILE] LabelEditScreen.kt
|
||||
// [SEMANTICS] ui, screen, label, edit
|
||||
// [SEMANTICS] app, ui, screen, edit, label
|
||||
|
||||
package com.homebox.lens.ui.screen.labeledit
|
||||
|
||||
@@ -24,10 +24,10 @@ import com.homebox.lens.ui.components.LoadingOverlay
|
||||
// [ENTITY: Function('LabelEditScreen')]
|
||||
// [RELATION: Function('LabelEditScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelEditViewModel')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Редактирование метки".
|
||||
* @param labelId ID метки для редактирования или null для создания новой.
|
||||
* @param onBack Навигация назад.
|
||||
* @param onLabelSaved Действие после сохранения метки.
|
||||
* @summary Composable function for the "Edit Label" screen.
|
||||
* @param labelId The ID of the label to edit, or null to create a new one.
|
||||
* @param onBack Navigation back.
|
||||
* @param onLabelSaved Action after the label is saved.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.labeledit
|
||||
// [FILE] LabelEditViewModel.kt
|
||||
// [SEMANTICS] ui, viewmodel, label_management
|
||||
// [SEMANTICS] app, ui, viewmodel, edit, label
|
||||
|
||||
package com.homebox.lens.ui.screen.labeledit
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.labelslist
|
||||
// [FILE] LabelsListScreen.kt
|
||||
// [SEMANTICS] ui, labels_list, state_management, compose, dialog
|
||||
// [SEMANTICS] app, ui, screen, list, label
|
||||
package com.homebox.lens.ui.screen.labelslist
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -46,10 +46,10 @@ import timber.log.Timber
|
||||
// [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelsListViewModel')]
|
||||
// [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [Framework('NavController')]
|
||||
/**
|
||||
* @summary Отображает экран со списком всех меток.
|
||||
* @param currentRoute Текущий маршрут навигации.
|
||||
* @param navigationActions Объект, содержащий действия по навигации.
|
||||
* @param viewModel ViewModel, предоставляющая состояние UI для экрана меток.
|
||||
* @summary Displays the screen with a list of all labels.
|
||||
* @param currentRoute The current navigation route.
|
||||
* @param navigationActions The object containing navigation actions.
|
||||
* @param viewModel The ViewModel providing the UI state for the labels screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -116,10 +116,10 @@ fun LabelsListScreen(
|
||||
// [ENTITY: Function('LabelsList')]
|
||||
// [RELATION: Function('LabelsList')] -> [DEPENDS_ON] -> [DataClass('Label')]
|
||||
/**
|
||||
* @summary Composable-функция для отображения списка меток.
|
||||
* @param labels Список объектов `Label` для отображения.
|
||||
* @param onLabelClick Лямбда-функция, вызываемая при нажатии на элемент списка.
|
||||
* @param modifier Модификатор для настройки внешнего вида.
|
||||
* @summary Composable function for displaying a list of labels.
|
||||
* @param labels The list of `Label` objects to display.
|
||||
* @param onLabelClick A lambda function called when a list item is clicked.
|
||||
* @param modifier A modifier for customizing the appearance.
|
||||
*/
|
||||
@Composable
|
||||
private fun LabelsList(
|
||||
@@ -145,9 +145,9 @@ private fun LabelsList(
|
||||
// [ENTITY: Function('LabelListItem')]
|
||||
// [RELATION: Function('LabelListItem')] -> [DEPENDS_ON] -> [DataClass('Label')]
|
||||
/**
|
||||
* @summary Composable-функция для отображения одного элемента в списке меток.
|
||||
* @param label Объект `Label`, который нужно отобразить.
|
||||
* @param onClick Лямбда-функция, вызываемая при нажатии на элемент.
|
||||
* @summary Composable function for displaying a single item in the list of labels.
|
||||
* @param label The `Label` object to display.
|
||||
* @param onClick A lambda function called when the item is clicked.
|
||||
*/
|
||||
@Composable
|
||||
private fun LabelListItem(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.labelslist
|
||||
// [FILE] LabelsListUiState.kt
|
||||
// [SEMANTICS] ui_state, sealed_interface, contract
|
||||
// [SEMANTICS] app, ui, state, list, label
|
||||
package com.homebox.lens.ui.screen.labelslist
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -9,17 +9,17 @@ import com.homebox.lens.domain.model.Label
|
||||
|
||||
// [ENTITY: SealedInterface('LabelsListUiState')]
|
||||
/**
|
||||
* @summary Определяет все возможные состояния для UI экрана со списком меток.
|
||||
* @description Использование sealed-интерфейса позволяет исчерпывающе обрабатывать все состояния в Composable-функциях.
|
||||
* @summary Defines all possible states for the UI of the screen with a list of labels.
|
||||
* @description Using a sealed interface allows for exhaustive handling of all states in Composable functions.
|
||||
*/
|
||||
sealed interface LabelsListUiState {
|
||||
// [ENTITY: DataClass('Success')]
|
||||
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('Label')]
|
||||
/**
|
||||
* @summary Состояние успеха, содержит список меток и состояние диалога.
|
||||
* @param labels Список меток для отображения.
|
||||
* @param isShowingCreateDialog Флаг, показывающий, должен ли быть отображен диалог создания метки.
|
||||
* @invariant labels не может быть null.
|
||||
* @summary The success state, contains the list of labels and the state of the dialog.
|
||||
* @param labels The list of labels to display.
|
||||
* @param isShowingCreateDialog A flag indicating whether the label creation dialog should be displayed.
|
||||
* @invariant labels cannot be null.
|
||||
*/
|
||||
data class Success(
|
||||
val labels: List<Label>,
|
||||
@@ -29,17 +29,17 @@ sealed interface LabelsListUiState {
|
||||
|
||||
// [ENTITY: DataClass('Error')]
|
||||
/**
|
||||
* @summary Состояние ошибки.
|
||||
* @param message Текст ошибки для отображения пользователю.
|
||||
* @invariant message не может быть пустой.
|
||||
* @summary The error state.
|
||||
* @param message The error text to display to the user.
|
||||
* @invariant message cannot be empty.
|
||||
*/
|
||||
data class Error(val message: String) : LabelsListUiState
|
||||
// [END_ENTITY: DataClass('Error')]
|
||||
|
||||
// [ENTITY: Object('Loading')]
|
||||
/**
|
||||
* @summary Состояние загрузки данных.
|
||||
* @description Указывает, что идет процесс загрузки меток.
|
||||
* @summary The data loading state.
|
||||
* @description Indicates that the process of loading labels is in progress.
|
||||
*/
|
||||
data object Loading : LabelsListUiState
|
||||
// [END_ENTITY: Object('Loading')]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.labelslist
|
||||
// [FILE] LabelsListViewModel.kt
|
||||
// [SEMANTICS] ui_logic, labels_list, state_management, dialog_management
|
||||
// [SEMANTICS] app, ui, viewmodel, list, label
|
||||
package com.homebox.lens.ui.screen.labelslist
|
||||
|
||||
// [IMPORTS]
|
||||
@@ -21,9 +21,9 @@ import javax.inject.Inject
|
||||
// [RELATION: ViewModel('LabelsListViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLabelsUseCase')]
|
||||
// [RELATION: ViewModel('LabelsListViewModel')] -> [EMITS_STATE] -> [SealedInterface('LabelsListUiState')]
|
||||
/**
|
||||
* @summary ViewModel для экрана со списком меток.
|
||||
* @description Управляет состоянием экрана, загружает список меток, обрабатывает ошибки и управляет диалогом создания новой метки.
|
||||
* @invariant `uiState` всегда является одним из состояний, определенных в `LabelsListUiState`.
|
||||
* @summary ViewModel for the screen with a list of labels.
|
||||
* @description Manages the screen state, loads the list of labels, handles errors, and manages the dialog for creating a new label.
|
||||
* @invariant `uiState` is always one of the states defined in `LabelsListUiState`.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class LabelsListViewModel @Inject constructor(
|
||||
@@ -39,10 +39,10 @@ class LabelsListViewModel @Inject constructor(
|
||||
|
||||
// [ENTITY: Function('loadLabels')]
|
||||
/**
|
||||
* @summary Загружает список меток.
|
||||
* @description Выполняет `GetAllLabelsUseCase` и обновляет UI, переключая его
|
||||
* между состояниями `Loading`, `Success` и `Error`.
|
||||
* @sideeffect Асинхронно обновляет `_uiState`.
|
||||
* @summary Loads the list of labels.
|
||||
* @description Executes `GetAllLabelsUseCase` and updates the UI by switching it
|
||||
* between the `Loading`, `Success`, and `Error` states.
|
||||
* @sideeffect Asynchronously updates `_uiState`.
|
||||
*/
|
||||
fun loadLabels() {
|
||||
viewModelScope.launch {
|
||||
@@ -77,9 +77,9 @@ class LabelsListViewModel @Inject constructor(
|
||||
|
||||
// [ENTITY: Function('onShowCreateDialog')]
|
||||
/**
|
||||
* @summary Инициирует отображение диалога для создания метки.
|
||||
* @description Обновляет состояние `uiState`, устанавливая `isShowingCreateDialog` в `true`.
|
||||
* @sideeffect Обновляет `_uiState`.
|
||||
* @summary Initiates the display of the dialog for creating a label.
|
||||
* @description Updates the `uiState` by setting `isShowingCreateDialog` to `true`.
|
||||
* @sideeffect Updates `_uiState`.
|
||||
*/
|
||||
fun onShowCreateDialog() {
|
||||
Timber.i("[INFO][ACTION][show_create_dialog] Show create label dialog requested.")
|
||||
@@ -93,9 +93,9 @@ class LabelsListViewModel @Inject constructor(
|
||||
|
||||
// [ENTITY: Function('onDismissCreateDialog')]
|
||||
/**
|
||||
* @summary Скрывает диалог создания метки.
|
||||
* @description Обновляет состояние `uiState`, устанавливая `isShowingCreateDialog` в `false`.
|
||||
* @sideeffect Обновляет `_uiState`.
|
||||
* @summary Hides the label creation dialog.
|
||||
* @description Updates the `uiState` by setting `isShowingCreateDialog` to `false`.
|
||||
* @sideeffect Updates `_uiState`.
|
||||
*/
|
||||
fun onDismissCreateDialog() {
|
||||
Timber.i("[INFO][ACTION][dismiss_create_dialog] Dismiss create label dialog requested.")
|
||||
@@ -109,12 +109,12 @@ class LabelsListViewModel @Inject constructor(
|
||||
|
||||
// [ENTITY: Function('createLabel')]
|
||||
/**
|
||||
* @summary Создает новую метку. [MVP_SCOPE] ЗАГЛУШКА.
|
||||
* @description В текущей реализации (План Б, Этап 1), эта функция только логирует действие
|
||||
* и скрывает диалог. Реальная логика сохранения будет добавлена на следующем этапе.
|
||||
* @param name Название новой метки.
|
||||
* @precondition `name` не должен быть пустым.
|
||||
* @sideeffect Логирует действие, обновляет `_uiState`, чтобы скрыть диалог.
|
||||
* @summary Creates a new label. [MVP_SCOPE] STUB.
|
||||
* @description In the current implementation (Plan B, Stage 1), this function only logs the action
|
||||
* and hides the dialog. The actual save logic will be added in the next stage.
|
||||
* @param name The name of the new label.
|
||||
* @precondition `name` must not be blank.
|
||||
* @sideeffect Logs the action, updates `_uiState` to hide the dialog.
|
||||
*/
|
||||
fun createLabel(name: String) {
|
||||
require(name.isNotBlank()) { "[CONTRACT_VIOLATION] Label name cannot be blank." }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.locationedit
|
||||
// [FILE] LocationEditScreen.kt
|
||||
// [SEMANTICS] ui, screen, location, edit
|
||||
// [SEMANTICS] app, ui, screen, edit, location
|
||||
|
||||
package com.homebox.lens.ui.screen.locationedit
|
||||
|
||||
@@ -19,8 +19,8 @@ import com.homebox.lens.R
|
||||
|
||||
// [ENTITY: Function('LocationEditScreen')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Редактирование местоположения".
|
||||
* @param locationId ID местоположения для редактирования или "new" для создания.
|
||||
* @summary Composable function for the "Edit Location" screen.
|
||||
* @param locationId The ID of the location to edit, or "new" to create one.
|
||||
*/
|
||||
@Composable
|
||||
fun LocationEditScreen(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.locationslist
|
||||
// [FILE] LocationsListScreen.kt
|
||||
// [SEMANTICS] ui, screen, locations, list
|
||||
// [SEMANTICS] app, ui, screen, list, location
|
||||
|
||||
package com.homebox.lens.ui.screen.locationslist
|
||||
|
||||
@@ -56,12 +56,12 @@ import com.homebox.lens.ui.theme.HomeboxLensTheme
|
||||
// [RELATION: Function('LocationsListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('LocationsListScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Список местоположений".
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @param onLocationClick Лямбда-обработчик нажатия на местоположение.
|
||||
* @param onAddNewLocationClick Лямбда-обработчик нажатия на кнопку добавления нового местоположения.
|
||||
* @param viewModel ViewModel для этого экрана.
|
||||
* @summary Composable function for the "List of Locations" screen.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
* @param onLocationClick A lambda handler for clicking on a location.
|
||||
* @param onAddNewLocationClick A lambda handler for clicking the button to add a new location.
|
||||
* @param viewModel The ViewModel for this screen.
|
||||
*/
|
||||
@Composable
|
||||
fun LocationsListScreen(
|
||||
@@ -104,12 +104,12 @@ fun LocationsListScreen(
|
||||
// [ENTITY: Function('LocationsListContent')]
|
||||
// [RELATION: Function('LocationsListContent')] -> [CONSUMES_STATE] -> [SealedInterface('LocationsListUiState')]
|
||||
/**
|
||||
* @summary Отображает основной контент экрана в зависимости от `uiState`.
|
||||
* @param modifier Модификатор для стилизации.
|
||||
* @param uiState Текущее состояние UI.
|
||||
* @param onLocationClick Лямбда-обработчик нажатия на местоположение.
|
||||
* @param onEditLocation Лямбда-обработчик для редактирования местоположения.
|
||||
* @param onDeleteLocation Лямбда-обработчик для удаления местоположения.
|
||||
* @summary Displays the main content of the screen depending on the `uiState`.
|
||||
* @param modifier A modifier for styling.
|
||||
* @param uiState The current UI state.
|
||||
* @param onLocationClick A lambda handler for clicking on a location.
|
||||
* @param onEditLocation A lambda handler for editing a location.
|
||||
* @param onDeleteLocation A lambda handler for deleting a location.
|
||||
*/
|
||||
@Composable
|
||||
private fun LocationsListContent(
|
||||
@@ -167,11 +167,11 @@ private fun LocationsListContent(
|
||||
// [ENTITY: Function('LocationCard')]
|
||||
// [RELATION: Function('LocationCard')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
|
||||
/**
|
||||
* @summary Карточка для отображения одного местоположения.
|
||||
* @param location Данные о местоположении.
|
||||
* @param onClick Лямбда-обработчик нажатия на карточку.
|
||||
* @param onEditClick Лямбда-обработчик нажатия на "Редактировать".
|
||||
* @param onDeleteClick Лямбда-обработчик нажатия на "Удалить".
|
||||
* @summary Card for displaying a single location.
|
||||
* @param location The data about the location.
|
||||
* @param onClick A lambda handler for clicking on the card.
|
||||
* @param onEditClick A lambda handler for clicking "Edit".
|
||||
* @param onDeleteClick A lambda handler for clicking "Delete".
|
||||
*/
|
||||
@Composable
|
||||
private fun LocationCard(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.locationslist
|
||||
// [FILE] LocationsListUiState.kt
|
||||
// [SEMANTICS] ui, state, locations
|
||||
// [SEMANTICS] app, ui, state, list, location
|
||||
|
||||
package com.homebox.lens.ui.screen.locationslist
|
||||
|
||||
@@ -10,30 +10,30 @@ import com.homebox.lens.domain.model.LocationOutCount
|
||||
|
||||
// [ENTITY: SealedInterface('LocationsListUiState')]
|
||||
/**
|
||||
* @summary Определяет возможные состояния UI для экрана списка местоположений.
|
||||
* @summary Defines the possible UI states for the list of locations screen.
|
||||
* @see LocationsListViewModel
|
||||
*/
|
||||
sealed interface LocationsListUiState {
|
||||
// [ENTITY: DataClass('Success')]
|
||||
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
|
||||
/**
|
||||
* @summary Состояние успешной загрузки данных.
|
||||
* @param locations Список местоположений для отображения.
|
||||
* @summary The state of a successful data load.
|
||||
* @param locations The list of locations to display.
|
||||
*/
|
||||
data class Success(val locations: List<LocationOutCount>) : LocationsListUiState
|
||||
// [END_ENTITY: DataClass('Success')]
|
||||
|
||||
// [ENTITY: DataClass('Error')]
|
||||
/**
|
||||
* @summary Состояние ошибки.
|
||||
* @param message Сообщение об ошибке.
|
||||
* @summary The error state.
|
||||
* @param message The error message.
|
||||
*/
|
||||
data class Error(val message: String) : LocationsListUiState
|
||||
// [END_ENTITY: DataClass('Error')]
|
||||
|
||||
// [ENTITY: Object('Loading')]
|
||||
/**
|
||||
* @summary Состояние загрузки данных.
|
||||
* @summary The data loading state.
|
||||
*/
|
||||
object Loading : LocationsListUiState
|
||||
// [END_ENTITY: Object('Loading')]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.locationslist
|
||||
// [FILE] LocationsListViewModel.kt
|
||||
// [SEMANTICS] ui, viewmodel, locations, hilt
|
||||
// [SEMANTICS] app, ui, viewmodel, list, location
|
||||
|
||||
package com.homebox.lens.ui.screen.locationslist
|
||||
|
||||
@@ -21,10 +21,10 @@ import javax.inject.Inject
|
||||
// [RELATION: ViewModel('LocationsListViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLocationsUseCase')]
|
||||
// [RELATION: ViewModel('LocationsListViewModel')] -> [EMITS_STATE] -> [SealedInterface('LocationsListUiState')]
|
||||
/**
|
||||
* @summary ViewModel для экрана списка местоположений.
|
||||
* @param getAllLocationsUseCase Use case для получения всех местоположений.
|
||||
* @property uiState Поток, содержащий текущее состояние UI.
|
||||
* @invariant `uiState` всегда отражает результат последней операции загрузки.
|
||||
* @summary ViewModel for the list of locations screen.
|
||||
* @param getAllLocationsUseCase Use case for getting all locations.
|
||||
* @property uiState A flow containing the current UI state.
|
||||
* @invariant `uiState` always reflects the result of the last load operation.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class LocationsListViewModel @Inject constructor(
|
||||
@@ -40,8 +40,8 @@ class LocationsListViewModel @Inject constructor(
|
||||
|
||||
// [ENTITY: Function('loadLocations')]
|
||||
/**
|
||||
* @summary Загружает список местоположений из репозитория.
|
||||
* @sideeffect Обновляет `_uiState` в зависимости от результата: Loading -> Success/Error.
|
||||
* @summary Loads the list of locations from the repository.
|
||||
* @sideeffect Updates `_uiState` depending on the result: Loading -> Success/Error.
|
||||
*/
|
||||
fun loadLocations() {
|
||||
Timber.d("[DEBUG][ENTRYPOINT][loading_locations] Starting to load locations.")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.search
|
||||
// [FILE] SearchScreen.kt
|
||||
// [SEMANTICS] ui, screen, search
|
||||
// [SEMANTICS] app, ui, screen, search
|
||||
|
||||
package com.homebox.lens.ui.screen.search
|
||||
|
||||
@@ -17,9 +17,9 @@ import com.homebox.lens.ui.common.MainScaffold
|
||||
// [RELATION: Function('SearchScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('SearchScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Поиск".
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigationActions Объект с навигационными действиями.
|
||||
* @summary Composable function for the "Search" screen.
|
||||
* @param currentRoute The current route to highlight the active item in the Drawer.
|
||||
* @param navigationActions The object with navigation actions.
|
||||
*/
|
||||
@Composable
|
||||
fun SearchScreen(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.search
|
||||
// [FILE] SearchViewModel.kt
|
||||
// [SEMANTICS] ui, viewmodel, search
|
||||
// [SEMANTICS] app, ui, viewmodel, search
|
||||
package com.homebox.lens.ui.screen.search
|
||||
|
||||
// [IMPORTS]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.settings
|
||||
// [FILE] SettingsScreen.kt
|
||||
// [SEMANTICS] ui, screen, settings
|
||||
// [SEMANTICS] app, ui, screen, settings
|
||||
|
||||
package com.homebox.lens.ui.screen.settings
|
||||
|
||||
@@ -24,9 +24,9 @@ import com.homebox.lens.ui.common.MainScaffold
|
||||
// [ENTITY: Function('SettingsScreen')]
|
||||
// [RELATION: Function('SettingsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана настроек.
|
||||
* @param currentRoute Текущий маршрут навигации.
|
||||
* @param navigationActions Объект, содержащий действия по навигации.
|
||||
* @summary Composable function for the settings screen.
|
||||
* @param currentRoute The current navigation route.
|
||||
* @param navigationActions The object containing navigation actions.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.setup
|
||||
// [FILE] SetupScreen.kt
|
||||
// [SEMANTICS] ui, screen, setup, compose
|
||||
// [SEMANTICS] app, ui, screen, setup
|
||||
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
@@ -31,10 +31,10 @@ import com.homebox.lens.R
|
||||
// [RELATION: Function('SetupScreen')] -> [DEPENDS_ON] -> [ViewModel('SetupViewModel')]
|
||||
// [RELATION: Function('SetupScreen')] -> [CALLS] -> [Function('SetupScreenContent')]
|
||||
/**
|
||||
* @summary Главная Composable-функция для экрана настройки соединения с сервером.
|
||||
* @param viewModel ViewModel для этого экрана, предоставляется через Hilt.
|
||||
* @param onSetupComplete Лямбда, вызываемая после успешной настройки и входа.
|
||||
* @sideeffect Вызывает `onSetupComplete` при изменении `uiState.isSetupComplete`.
|
||||
* @summary The main Composable function for the server connection setup screen.
|
||||
* @param viewModel The ViewModel for this screen, provided by Hilt.
|
||||
* @param onSetupComplete A lambda invoked after successful setup and login.
|
||||
* @sideeffect Calls `onSetupComplete` when `uiState.isSetupComplete` changes.
|
||||
*/
|
||||
@Composable
|
||||
fun SetupScreen(
|
||||
@@ -60,12 +60,12 @@ fun SetupScreen(
|
||||
// [ENTITY: Function('SetupScreenContent')]
|
||||
// [RELATION: Function('SetupScreenContent')] -> [CONSUMES_STATE] -> [DataClass('SetupUiState')]
|
||||
/**
|
||||
* @summary Отображает контент экрана настройки: поля ввода и кнопку.
|
||||
* @param uiState Текущее состояние UI.
|
||||
* @param onServerUrlChange Лямбда-обработчик изменения URL сервера.
|
||||
* @param onUsernameChange Лямбда-обработчик изменения имени пользователя.
|
||||
* @param onPasswordChange Лямбда-обработчик изменения пароля.
|
||||
* @param onConnectClick Лямбда-обработчик нажатия на кнопку "Подключиться".
|
||||
* @summary Displays the content of the setup screen: input fields and a button.
|
||||
* @param uiState The current UI state.
|
||||
* @param onServerUrlChange A lambda handler for changing the server URL.
|
||||
* @param onUsernameChange A lambda handler for changing the username.
|
||||
* @param onPasswordChange A lambda handler for changing the password.
|
||||
* @param onConnectClick A lambda handler for clicking the "Connect" button.
|
||||
*/
|
||||
@Composable
|
||||
private fun SetupScreenContent(
|
||||
@@ -75,11 +75,7 @@ private fun SetupScreenContent(
|
||||
onPasswordChange: (String) -> Unit,
|
||||
onConnectClick: () -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(title = { Text(stringResource(id = R.string.setup_title)) })
|
||||
}
|
||||
) { paddingValues ->
|
||||
Scaffold { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -96,8 +92,12 @@ private fun SetupScreenContent(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.setup_title),
|
||||
style = MaterialTheme.typography.headlineLarge,
|
||||
fontSize = 28.sp // Adjust font size as needed
|
||||
style = MaterialTheme.typography.headlineLarge
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "Enter your Homebox server details to connect.",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
@@ -106,8 +106,7 @@ private fun SetupScreenContent(
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = uiState.serverUrl,
|
||||
@@ -115,14 +114,14 @@ private fun SetupScreenContent(
|
||||
label = { Text(stringResource(id = R.string.setup_server_url_label)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = uiState.username,
|
||||
onValueChange = onUsernameChange,
|
||||
label = { Text(stringResource(id = R.string.setup_username_label)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = uiState.password,
|
||||
onValueChange = onPasswordChange,
|
||||
@@ -138,7 +137,7 @@ private fun SetupScreenContent(
|
||||
enabled = !uiState.isLoading,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp) // Make button more prominent
|
||||
.height(50.dp)
|
||||
) {
|
||||
if (uiState.isLoading) {
|
||||
CircularProgressIndicator(
|
||||
@@ -146,15 +145,14 @@ private fun SetupScreenContent(
|
||||
color = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
} else {
|
||||
Text(stringResource(id = R.string.setup_connect_button), fontSize = 18.sp)
|
||||
Text(stringResource(id = R.string.setup_connect_button))
|
||||
}
|
||||
}
|
||||
uiState.error?.let {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = it,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.splash
|
||||
// [FILE] SplashScreen.kt
|
||||
// [SEMANTICS] ui, screen, splash, navigation, authentication_flow
|
||||
|
||||
// [SEMANTICS] app, ui, screen, splash
|
||||
package com.homebox.lens.ui.screen.splash
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -16,42 +16,44 @@ import androidx.navigation.NavController
|
||||
import com.homebox.lens.navigation.Screen
|
||||
import com.homebox.lens.ui.screen.setup.SetupViewModel
|
||||
import timber.log.Timber
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: Function('SplashScreen')]
|
||||
// [ENTITY: Composable('SplashScreen')]
|
||||
// [RELATION: Composable('SplashScreen')] -> [DEPENDS_ON] -> [Framework('NavController')]
|
||||
// [RELATION: Composable('SplashScreen')] -> [DEPENDS_ON] -> [ViewModel('SetupViewModel')]
|
||||
/**
|
||||
* @summary Displays a splash screen while checking if credentials are saved.
|
||||
* @param navController The NavController for navigation.
|
||||
* @param viewModel The SetupViewModel to check credential status.
|
||||
* @sideeffect Navigates to either SetupScreen or DashboardScreen based on credential status.
|
||||
* @summary A splash screen that checks for saved credentials and navigates accordingly.
|
||||
* @param navController The navigation controller for navigating to the next screen.
|
||||
* @param viewModel The view model for checking credentials.
|
||||
* @sideeffect Navigates to either the Setup or Dashboard screen.
|
||||
*/
|
||||
@Composable
|
||||
fun SplashScreen(
|
||||
navController: NavController,
|
||||
viewModel: SetupViewModel = hiltViewModel()
|
||||
) {
|
||||
Timber.d("[DEBUG][ENTRYPOINT][splash_screen_composable] SplashScreen entered.")
|
||||
|
||||
LaunchedEffect(key1 = true) {
|
||||
Timber.i("[INFO][ACTION][checking_credentials_on_launch] Checking if credentials are saved on launch.")
|
||||
val credentialsSaved = viewModel.areCredentialsSaved()
|
||||
if (credentialsSaved) {
|
||||
Timber.i("[INFO][ACTION][credentials_found_navigating_dashboard] Credentials found, navigating to Dashboard.")
|
||||
navController.navigate(Screen.Dashboard.route) {
|
||||
popUpTo(Screen.Splash.route) { inclusive = true }
|
||||
}
|
||||
} else {
|
||||
Timber.i("[INFO][ACTION][no_credentials_found_navigating_setup] No credentials found, navigating to Setup.")
|
||||
navController.navigate(Screen.Setup.route) {
|
||||
popUpTo(Screen.Splash.route) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Timber.d("[DEBUG][ACTION][checking_credentials] Checking for saved credentials on splash screen.")
|
||||
val areCredentialsSaved = viewModel.areCredentialsSaved()
|
||||
val destination = if (areCredentialsSaved) {
|
||||
Timber.d("[DEBUG][SUCCESS][credentials_found] Credentials found, navigating to Dashboard.")
|
||||
Screen.Dashboard.route
|
||||
} else {
|
||||
Timber.d("[DEBUG][FALLBACK][no_credentials] No credentials found, navigating to Setup.")
|
||||
Screen.Setup.route
|
||||
}
|
||||
|
||||
navController.navigate(destination) {
|
||||
popUpTo(Screen.Splash.route) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('SplashScreen')]
|
||||
// [END_ENTITY: Composable('SplashScreen')]
|
||||
// [END_FILE_SplashScreen.kt]
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.theme
|
||||
// [FILE] Color.kt
|
||||
// [SEMANTICS] ui, theme, color
|
||||
// [SEMANTICS] app, ui, theme, color
|
||||
package com.homebox.lens.ui.theme
|
||||
|
||||
// [IMPORTS]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.theme
|
||||
// [FILE] Theme.kt
|
||||
// [SEMANTICS] ui, theme
|
||||
// [SEMANTICS] app, ui, theme
|
||||
package com.homebox.lens.ui.theme
|
||||
|
||||
// [IMPORTS]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// [PACKAGE] com.homebox.lens.ui.theme
|
||||
// [FILE] Typography.kt
|
||||
// [SEMANTICS] ui, theme, typography
|
||||
// [SEMANTICS] app, ui, theme, typography
|
||||
package com.homebox.lens.ui.theme
|
||||
|
||||
// [IMPORTS]
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
<!-- Content Descriptions -->
|
||||
<string name="cd_open_navigation_drawer">Открыть боковое меню</string>
|
||||
<string name="setup_subtitle">Enter your Homebox server details to connect.</string>
|
||||
<string name="cd_scan_qr_code">Сканировать QR-код</string>
|
||||
<string name="cd_navigate_back">Вернуться назад</string>
|
||||
<string name="cd_add_new_location">Добавить новую локацию</string>
|
||||
|
||||
Reference in New Issue
Block a user