基本架構寫好後,每次加新功能都是從 use case 開始,AI 寫扣的時代,規劃架構依照重要,不然就會產生很多風格不一樣的頁面
data class CategoryManagementUiState(
val categories: List<Category> = emptyList(),
val isLoading: Boolean = true
)
@HiltViewModel
class CategoryManagementViewModel @Inject constructor(
private val noteUseCases: NoteUseCases
) : ViewModel() {
val uiState: StateFlow<CategoryManagementUiState> = noteUseCases.getCategories()
.map { CategoryManagementUiState(categories = it, isLoading = false) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = CategoryManagementUiState(isLoading = true)
)
fun renameCategory(category: Category, newName: String) {
viewModelScope.launch {
noteUseCases.saveCategory(category.copy(name = newName))
}
}
fun deleteCategory(category: Category) {
viewModelScope.launch {
noteUseCases.deleteCategory(category) // Need to add this use case
}
}
}
class DeleteCategoryUseCase @Inject constructor(
private val repository: NoteRepository
) {
suspend operator fun invoke(category: Category) {
repository.deleteCategory(category)
}
}
@Composable
fun CategoryManagementScreen(
modifier: Modifier = Modifier,
viewModel: CategoryManagementViewModel = hiltViewModel(),
onNavigateBack: () -> Unit
) {
val uiState by viewModel.uiState.collectAsState()
CategoryManagementScreenContent(
modifier = modifier,
uiState = uiState,
onRenameCategory = viewModel::renameCategory,
onDeleteCategory = viewModel::deleteCategory,
onNavigateBack = onNavigateBack
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CategoryManagementScreenContent(
modifier: Modifier = Modifier,
uiState: CategoryManagementUiState,
onRenameCategory: (Category, String) -> Unit,
onDeleteCategory: (Category) -> Unit,
onNavigateBack: () -> Unit
) {
var categoryToRename by remember { mutableStateOf<Category?>(null) }
var categoryToDelete by remember { mutableStateOf<Category?>(null) }
Scaffold(
modifier = modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = { Text("管理分類") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "返回")
}
}
)
}
) { innerPadding ->
Box(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
if (uiState.isLoading) {
CircularProgressIndicator()
} else {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(uiState.categories, key = { it.id }) {
ListItem(
headlineContent = { Text(category.name) },
trailingContent = {
Row {
IconButton(onClick = { categoryToRename = category }) {
Icon(Icons.Default.Edit, contentDescription = "重新命名")
}
IconButton(onClick = { categoryToDelete = category }) {
Icon(Icons.Default.Delete, contentDescription = "刪除")
}
}
}
)
}
}
}
}
}
// Rename Dialog
if (categoryToRename != null) {
RenameCategoryDialog(
category = categoryToRename!!,
onDismiss = { categoryToRename = null },
onConfirm = {
onRenameCategory(categoryToRename!!, newName)
categoryToRename = null
}
)
}
// Delete Dialog
if (categoryToDelete != null) {
DeleteCategoryDialog(
category = categoryToDelete!!,
onDismiss = { categoryToDelete = null },
onConfirm = {
onDeleteCategory(categoryToDelete!!)
categoryToDelete = null
}
)
}
}
@Composable
private fun RenameCategoryDialog(
category: Category,
onDismiss: () -> Unit,
onConfirm: (String) -> Unit
) {
var newName by remember { mutableStateOf(category.name) }
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("重新命名分類") },
text = {
TextField(
value = newName,
onValueChange = { newName = it },
label = { Text("分類名稱") }
)
},
confirmButton = {
Button(onClick = { onConfirm(newName) }) {
Text("確定")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text("取消")
}
}
)
}
@Composable
private fun DeleteCategoryDialog(
category: Category,
onDismiss: () -> Unit,
onConfirm: () -> Unit
) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("刪除分類") },
text = { Text("您確定要刪除「${category.name}」嗎?\n屬於此分類的筆記將會被保留並標示為「無分類」。") },
confirmButton = {
Button(onClick = onConfirm) {
Text("刪除")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text("取消")
}
}
)
}
@Preview(showBackground = true)
@Composable
private fun CategoryManagementScreenPreview() {
AndroidTemplateTheme {
CategoryManagementScreenContent(
uiState = CategoryManagementUiState(
categories = listOf(Category(1, "工作", 0), Category(2, "生活", 1)),
isLoading = false
),
onRenameCategory = { _, _ -> },
onDeleteCategory = {},
onNavigateBack = {}
)
}
}
Text(
text = "資料管理",
style = MaterialTheme.typography.titleLarge
)
Button(onClick = onNavigateToCategoryManagement) {
Text("管理分類")
}