Labels
This commit is contained in:
211
tasks/completed/003_implement_labels_screen_ui.xml
Normal file
211
tasks/completed/003_implement_labels_screen_ui.xml
Normal file
@@ -0,0 +1,211 @@
|
||||
<!-- 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>
|
||||
Reference in New Issue
Block a user