Files
homebox_lens/tasks/completed/003_implement_labels_screen_ui.xml
2025-08-14 15:34:05 +03:00

211 lines
9.5 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- tasks/003_implement_labels_screen_ui.xml -->
<TASK status="completed">
<WORK_ORDER id="task-20250812-115003">
<ACTION>MODIFY_CODE</ACTION>
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</TARGET_FILE>
<GOAL>
Реализовать UI для экрана "Метки" (LabelsListScreen), заменив заглушку.
Экран должен получать данные от LabelsListViewModel, отображать список меток в LazyColumn,
а также содержать TopAppBar с кнопкой "назад" и FloatingActionButton для добавления новой метки,
в полном соответствии со спецификацией screen_labels_list.
</GOAL>
<CONTEXT_FILES>
<FILE>tech_spec.txt</FILE>
<FILE>project_structure.txt</FILE>
<FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListViewModel.kt</FILE>
</CONTEXT_FILES>
<CONTRACT>
<CONSTRAINTS>
<CONSTRAINT>Полностью заменить содержимое файла `LabelsListScreen.kt`.</CONSTRAINT>
<CONSTRAINT>Главная функция `LabelsListScreen` должна получать `LabelsListViewModel` через `hiltViewModel()`.</CONSTRAINT>
<CONSTRAINT>Состояние UI должно собираться из `viewModel.uiState` с использованием `collectAsStateWithLifecycle`.</CONSTRAINT>
<CONSTRAINT>Для отображения списка должен использоваться `LazyColumn`.</CONSTRAINT>
<CONSTRAINT>Каждый элемент списка должен быть реализован в отдельном Composable `LabelListItem`.</CONSTRAINT>
<CONSTRAINT>`TopAppBar` должен содержать `IconButton` для навигации назад.</CONSTRAINT>
<CONSTRAINT>`Scaffold` должен содержать `FloatingActionButton`.</CONSTRAINT>
</CONSTRAINTS>
</CONTRACT>
<PAYLOAD mode="FULL_CONTENT">
<![CDATA[
// [FILE] app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt
// [PACKAGE]
package com.homebox.lens.ui.screen.labelslist
// [IMPORTS]
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Label
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.homebox.lens.domain.model.Label
import com.homebox.lens.ui.theme.HomeboxLensTheme
// [COMPOSABLE_FUNCTION] LabelsListScreen (Stateful)
/**
* [CONTRACT]
* Контейнерный Composable для экрана "Метки", управляющий состоянием.
* Он получает данные от ViewModel и передает их в state-less Composable `LabelsListContent`.
*
* @param viewModel ViewModel, предоставляемая Hilt, для доступа к бизнес-логике и состоянию UI.
* @param onNavigateBack Лямбда для обработки действия "назад".
* @param onLabelClick Лямбда для обработки нажатия на метку, передает ID метки.
* @param onAddLabelClick Лямбда для обработки нажатия на FAB.
* @sideeffect Получает состояние UI (`uiState`) из `viewModel`.
* @sideeffect Вызывает навигационные лямбды в ответ на действия пользователя.
*/
@Composable
fun LabelsListScreen(
viewModel: LabelsListViewModel = hiltViewModel(),
onNavigateBack: () -> Unit,
onLabelClick: (String) -> Unit,
onAddLabelClick: () -> Unit
) {
// [ACTION] Сбор состояния из ViewModel
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
LabelsListContent(
labels = uiState.labels,
onNavigateBack = onNavigateBack,
onLabelClick = onLabelClick,
onAddLabelClick = onAddLabelClick
)
// [COHERENCE_CHECK_PASSED] Состояние передается в stateless composable.
}
// [END_FUNCTION]
// [COMPOSABLE_FUNCTION] LabelsListContent (Stateless)
/**
* [CONTRACT]
* Отображает UI для экрана "Метки". Этот Composable не имеет своего состояния (stateless).
*
* @param labels Список объектов `Label` для отображения.
* @param onNavigateBack Лямбда для обработки действия "назад".
* @param onLabelClick Лямбда для обработки нажатия на метку.
* @param onAddLabelClick Лямбда для обработки нажатия на FAB.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LabelsListContent(
labels: List<Label>,
onNavigateBack: () -> Unit,
onLabelClick: (String) -> Unit,
onAddLabelClick: () -> Unit
) {
// [CORE-LOGIC] Основная разметка экрана
Scaffold(
topBar = {
TopAppBar(
title = { Text("Метки") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Назад" // TODO: Заменить на stringResource
)
}
}
)
},
floatingActionButton = {
FloatingActionButton(onClick = onAddLabelClick) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Добавить метку" // TODO: Заменить на stringResource
)
}
}
) { innerPadding ->
// [CORE-LOGIC] Список меток
LazyColumn(modifier = Modifier.padding(innerPadding)) {
items(items = labels, key = { it.id }) { label ->
LabelListItem(
label = label,
onClick = { onLabelClick(label.id) }
)
}
}
}
}
// [END_FUNCTION]
// [HELPER] LabelListItem
/**
* [CONTRACT]
* Отображает один элемент списка для метки.
*
* @param label Объект `Label`, данные которого нужно отобразить.
* @param onClick Лямбда, вызываемая при нажатии на элемент.
*/
@Composable
private fun LabelListItem(
label: Label,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
ListItem(
headlineContent = { Text(label.name) },
leadingContent = {
Icon(
imageVector = Icons.Filled.Label,
contentDescription = null
)
},
modifier = modifier.clickable(onClick = onClick)
)
}
// [END_FUNCTION]
// [PREVIEW]
@Preview(showBackground = true)
@Composable
private fun LabelsListContentPreview() {
val sampleLabels = listOf(
Label(id = "1", name = "Электроника", color = "#FF0000"),
Label(id = "2", name = "Книги", color = "#00FF00"),
Label(id = "3", name = "Инструменты", color = "#0000FF")
)
HomeboxLensTheme {
LabelsListContent(
labels = sampleLabels,
onNavigateBack = {},
onLabelClick = {},
onAddLabelClick = {}
)
}
}
// [END_PREVIEW]
// [END_FILE]
]]>
</PAYLOAD>
<IMPLEMENTATION_HINTS>
<HINT>Это задание заменяет весь контент файла `app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt`.</HINT>
<HINT>Основная логика разделена на два Composable: `LabelsListScreen` (stateful) и `LabelsListContent` (stateless), что является хорошей практикой.</HINT>
<HINT>Функция `LabelsListScreen` отвечает за взаимодействие с ViewModel.</HINT>
<HINT>Функция `LabelsListContent` отвечает исключительно за отображение UI на основе переданных данных.</HINT>
<HINT>Убедись, что все импорты, указанные в секции [IMPORTS], добавлены корректно. Особенно важны `hiltViewModel` и `collectAsStateWithLifecycle`.</HINT>
</IMPLEMENTATION_HINTS>
</WORK_ORDER>
</TASK>