UI: refactor BottomBarConfig.ModelsManagement APIs
This commit is contained in:
parent
63fc56d603
commit
225c5435c5
|
|
@ -221,37 +221,41 @@ fun AppContent(
|
|||
) { uri -> uri?.let { modelsManagementViewModel.localModelFileSelected(it) } }
|
||||
|
||||
BottomBarConfig.ModelsManagement(
|
||||
isMultiSelectionMode = isMultiSelectionMode,
|
||||
selectedModels = selectedModels,
|
||||
onSelectAll = { modelsManagementViewModel.selectAllModels() },
|
||||
onDeselectAll = { modelsManagementViewModel.clearSelectedModels() },
|
||||
onDeleteSelected = {
|
||||
if (selectedModels.isNotEmpty()) {
|
||||
modelsManagementViewModel.batchDeletionClicked(selectedModels.toMap())
|
||||
sorting = BottomBarConfig.ModelsManagement.SortingConfig(
|
||||
currentOrder = sortOrder,
|
||||
isMenuVisible = showSortMenu,
|
||||
toggleMenu = { show -> modelsManagementViewModel.toggleSortMenu(show) },
|
||||
selectOrder = {
|
||||
modelsManagementViewModel.setSortOrder(it)
|
||||
modelsManagementViewModel.toggleSortMenu(false)
|
||||
}
|
||||
},
|
||||
onSortClicked = { modelsManagementViewModel.toggleSortMenu(true) },
|
||||
onFilterClicked = { /* TODO: implement filtering */ },
|
||||
onDeleteModeClicked = { modelsManagementViewModel.setMultiSelectionMode(true) },
|
||||
onAddModelClicked = { modelsManagementViewModel.toggleImportMenu(true) },
|
||||
onExitSelectionMode = { modelsManagementViewModel.setMultiSelectionMode(false) },
|
||||
showSortMenu = showSortMenu,
|
||||
onSortMenuDismissed = { modelsManagementViewModel.toggleSortMenu(false) },
|
||||
currentSortOrder = sortOrder,
|
||||
onSortOptionSelected = {
|
||||
modelsManagementViewModel.setSortOrder(it)
|
||||
modelsManagementViewModel.toggleSortMenu(false)
|
||||
},
|
||||
showImportModelMenu = showImportModelMenu,
|
||||
onImportMenuDismissed = { modelsManagementViewModel.toggleImportMenu(false) },
|
||||
onImportLocalModelClicked = {
|
||||
fileLauncher.launch(arrayOf("application/octet-stream", "*/*"))
|
||||
modelsManagementViewModel.toggleImportMenu(false)
|
||||
},
|
||||
onImportHuggingFaceClicked = {
|
||||
modelsManagementViewModel.importFromHuggingFace()
|
||||
modelsManagementViewModel.toggleImportMenu(false)
|
||||
}
|
||||
),
|
||||
filtering = BottomBarConfig.ModelsManagement.FilteringConfig(
|
||||
onClick = { /* TODO: implement filtering */ },
|
||||
),
|
||||
selection = BottomBarConfig.ModelsManagement.SelectionConfig(
|
||||
isActive = isMultiSelectionMode,
|
||||
toggleMode = { enabled -> modelsManagementViewModel.toggleSelectionMode(enabled) },
|
||||
selectedModels = selectedModels,
|
||||
toggleAllSelection = { selectAll -> modelsManagementViewModel.toggleAllSelection(selectAll) },
|
||||
deleteSelected = {
|
||||
if (selectedModels.isNotEmpty()) {
|
||||
modelsManagementViewModel.batchDeletionClicked(selectedModels)
|
||||
}
|
||||
},
|
||||
),
|
||||
importing = BottomBarConfig.ModelsManagement.ImportConfig(
|
||||
isMenuVisible = showImportModelMenu,
|
||||
toggleMenu = { show -> modelsManagementViewModel.toggleImportMenu(show) },
|
||||
importFromLocal = {
|
||||
fileLauncher.launch(arrayOf("application/octet-stream", "*/*"))
|
||||
modelsManagementViewModel.toggleImportMenu(false)
|
||||
},
|
||||
importFromHuggingFace = {
|
||||
modelsManagementViewModel.importFromHuggingFace()
|
||||
modelsManagementViewModel.toggleImportMenu(false)
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> BottomBarConfig.None
|
||||
|
|
|
|||
|
|
@ -62,30 +62,16 @@ fun AppScaffold(
|
|||
}
|
||||
|
||||
val bottomBar: @Composable () -> Unit = {
|
||||
when (bottomBarConfig) {
|
||||
when (val config = bottomBarConfig) {
|
||||
is BottomBarConfig.None -> {
|
||||
/* No bottom bar */
|
||||
}
|
||||
is BottomBarConfig.ModelsManagement -> {
|
||||
ModelsManagementBottomBar(
|
||||
isMultiSelectionMode = bottomBarConfig.isMultiSelectionMode,
|
||||
selectedModels = bottomBarConfig.selectedModels,
|
||||
onSelectAll = bottomBarConfig.onSelectAll,
|
||||
onDeselectAll = bottomBarConfig.onDeselectAll,
|
||||
onDeleteSelected = bottomBarConfig.onDeleteSelected,
|
||||
onSortClicked = bottomBarConfig.onSortClicked,
|
||||
onFilterClicked = bottomBarConfig.onFilterClicked,
|
||||
onDeleteModeClicked = bottomBarConfig.onDeleteModeClicked,
|
||||
onAddModelClicked = bottomBarConfig.onAddModelClicked,
|
||||
onExitSelectionMode = bottomBarConfig.onExitSelectionMode,
|
||||
showSortMenu = bottomBarConfig.showSortMenu,
|
||||
onSortMenuDismissed = bottomBarConfig.onSortMenuDismissed,
|
||||
currentSortOrder = bottomBarConfig.currentSortOrder,
|
||||
onSortOptionSelected = bottomBarConfig.onSortOptionSelected,
|
||||
showImportModelMenu = bottomBarConfig.showImportModelMenu,
|
||||
onImportMenuDismissed = bottomBarConfig.onImportMenuDismissed,
|
||||
onImportLocalModelClicked = bottomBarConfig.onImportLocalModelClicked,
|
||||
onImportHuggingFaceClicked = bottomBarConfig.onImportHuggingFaceClicked
|
||||
sorting = config.sorting,
|
||||
filtering = config.filtering,
|
||||
selection = config.selection,
|
||||
importing = config.importing,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.example.llama.R
|
||||
import com.example.llama.revamp.data.model.ModelInfo
|
||||
import com.example.llama.revamp.ui.components.BottomBarConfig.ModelsManagement
|
||||
import com.example.llama.revamp.viewmodel.ModelSortOrder
|
||||
|
||||
/**
|
||||
|
|
@ -37,62 +38,60 @@ sealed class BottomBarConfig {
|
|||
object None : BottomBarConfig()
|
||||
|
||||
data class ModelsManagement(
|
||||
val isMultiSelectionMode: Boolean,
|
||||
val selectedModels: Map<String, ModelInfo>,
|
||||
val onSelectAll: () -> Unit,
|
||||
val onDeselectAll: () -> Unit,
|
||||
val onDeleteSelected: () -> Unit,
|
||||
val onSortClicked: () -> Unit,
|
||||
val onFilterClicked: () -> Unit,
|
||||
val onDeleteModeClicked: () -> Unit,
|
||||
val onAddModelClicked: () -> Unit,
|
||||
val onExitSelectionMode: () -> Unit,
|
||||
val showSortMenu: Boolean,
|
||||
val onSortMenuDismissed: () -> Unit,
|
||||
val currentSortOrder: ModelSortOrder,
|
||||
val onSortOptionSelected: (ModelSortOrder) -> Unit,
|
||||
val showImportModelMenu: Boolean,
|
||||
val onImportMenuDismissed: () -> Unit,
|
||||
val onImportLocalModelClicked: () -> Unit,
|
||||
val onImportHuggingFaceClicked: () -> Unit
|
||||
) : BottomBarConfig()
|
||||
val sorting: SortingConfig,
|
||||
val filtering: FilteringConfig,
|
||||
val selection: SelectionConfig,
|
||||
val importing: ImportConfig
|
||||
) : BottomBarConfig() {
|
||||
data class SortingConfig(
|
||||
val currentOrder: ModelSortOrder,
|
||||
val isMenuVisible: Boolean,
|
||||
val toggleMenu: (Boolean) -> Unit,
|
||||
val selectOrder: (ModelSortOrder) -> Unit
|
||||
)
|
||||
|
||||
data class FilteringConfig(
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
|
||||
data class SelectionConfig(
|
||||
val isActive: Boolean,
|
||||
val toggleMode: (Boolean) -> Unit,
|
||||
val selectedModels: Map<String, ModelInfo>,
|
||||
val toggleAllSelection: (Boolean) -> Unit,
|
||||
val deleteSelected: () -> Unit
|
||||
)
|
||||
|
||||
data class ImportConfig(
|
||||
val isMenuVisible: Boolean,
|
||||
val toggleMenu: (Boolean) -> Unit,
|
||||
val importFromLocal: () -> Unit,
|
||||
val importFromHuggingFace: () -> Unit
|
||||
)
|
||||
}
|
||||
|
||||
// TODO-han.yin: add more bottom bar types here
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ModelsManagementBottomBar(
|
||||
isMultiSelectionMode: Boolean,
|
||||
selectedModels: Map<String, ModelInfo>,
|
||||
onSelectAll: () -> Unit,
|
||||
onDeselectAll: () -> Unit,
|
||||
onDeleteSelected: () -> Unit,
|
||||
onSortClicked: () -> Unit,
|
||||
onFilterClicked: () -> Unit,
|
||||
onDeleteModeClicked: () -> Unit,
|
||||
onAddModelClicked: () -> Unit,
|
||||
onExitSelectionMode: () -> Unit,
|
||||
showSortMenu: Boolean,
|
||||
onSortMenuDismissed: () -> Unit,
|
||||
currentSortOrder: ModelSortOrder,
|
||||
onSortOptionSelected: (ModelSortOrder) -> Unit,
|
||||
showImportModelMenu: Boolean,
|
||||
onImportMenuDismissed: () -> Unit,
|
||||
onImportLocalModelClicked: () -> Unit,
|
||||
onImportHuggingFaceClicked: () -> Unit
|
||||
sorting: ModelsManagement.SortingConfig,
|
||||
filtering: ModelsManagement.FilteringConfig,
|
||||
selection: ModelsManagement.SelectionConfig,
|
||||
importing: ModelsManagement.ImportConfig
|
||||
) {
|
||||
BottomAppBar(
|
||||
actions = {
|
||||
if (isMultiSelectionMode) {
|
||||
if (selection.isActive) {
|
||||
// Multi-selection mode actions
|
||||
IconButton(onClick = onSelectAll) {
|
||||
IconButton(onClick = { selection.toggleAllSelection(true) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.SelectAll,
|
||||
contentDescription = "Select all"
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = onDeselectAll) {
|
||||
IconButton(onClick = { selection.toggleAllSelection(false) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ClearAll,
|
||||
contentDescription = "Deselect all"
|
||||
|
|
@ -100,13 +99,13 @@ fun ModelsManagementBottomBar(
|
|||
}
|
||||
|
||||
IconButton(
|
||||
onClick = onDeleteSelected,
|
||||
enabled = selectedModels.isNotEmpty()
|
||||
onClick = selection.deleteSelected,
|
||||
enabled = selection.selectedModels.isNotEmpty()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = "Delete selected",
|
||||
tint = if (selectedModels.isNotEmpty())
|
||||
tint = if (selection.selectedModels.isNotEmpty())
|
||||
MaterialTheme.colorScheme.error
|
||||
else
|
||||
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f)
|
||||
|
|
@ -114,7 +113,7 @@ fun ModelsManagementBottomBar(
|
|||
}
|
||||
} else {
|
||||
// Default mode actions
|
||||
IconButton(onClick = onSortClicked) {
|
||||
IconButton(onClick = { sorting.toggleMenu(true) }) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Sort,
|
||||
contentDescription = "Sort models"
|
||||
|
|
@ -123,78 +122,78 @@ fun ModelsManagementBottomBar(
|
|||
|
||||
// Sort dropdown menu
|
||||
DropdownMenu(
|
||||
expanded = showSortMenu,
|
||||
onDismissRequest = onSortMenuDismissed
|
||||
expanded = sorting.isMenuVisible,
|
||||
onDismissRequest = { sorting.toggleMenu(false) }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Name (A-Z)") },
|
||||
trailingIcon = {
|
||||
if (currentSortOrder == ModelSortOrder.NAME_ASC)
|
||||
if (sorting.currentOrder == ModelSortOrder.NAME_ASC)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Sort by name in ascending order, selected"
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onSortOptionSelected(ModelSortOrder.NAME_ASC)
|
||||
sorting.selectOrder(ModelSortOrder.NAME_ASC)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Name (Z-A)") },
|
||||
trailingIcon = {
|
||||
if (currentSortOrder == ModelSortOrder.NAME_DESC)
|
||||
if (sorting.currentOrder == ModelSortOrder.NAME_DESC)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Sort by name in descending order, selected"
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onSortOptionSelected(ModelSortOrder.NAME_DESC)
|
||||
sorting.selectOrder(ModelSortOrder.NAME_DESC)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Size (Smallest first)") },
|
||||
trailingIcon = {
|
||||
if (currentSortOrder == ModelSortOrder.SIZE_ASC)
|
||||
if (sorting.currentOrder == ModelSortOrder.SIZE_ASC)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Sort by size in ascending order, selected"
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onSortOptionSelected(ModelSortOrder.SIZE_ASC)
|
||||
sorting.selectOrder(ModelSortOrder.SIZE_ASC)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Size (Largest first)") },
|
||||
trailingIcon = {
|
||||
if (currentSortOrder == ModelSortOrder.SIZE_DESC)
|
||||
if (sorting.currentOrder == ModelSortOrder.SIZE_DESC)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Sort by size in descending order, selected"
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onSortOptionSelected(ModelSortOrder.SIZE_DESC)
|
||||
sorting.selectOrder(ModelSortOrder.SIZE_DESC)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Last used") },
|
||||
trailingIcon = {
|
||||
if (currentSortOrder == ModelSortOrder.LAST_USED)
|
||||
if (sorting.currentOrder == ModelSortOrder.LAST_USED)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Sort by last used, selected"
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onSortOptionSelected(ModelSortOrder.LAST_USED)
|
||||
sorting.selectOrder(ModelSortOrder.LAST_USED)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = onFilterClicked
|
||||
onClick = filtering.onClick
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.FilterAlt,
|
||||
|
|
@ -202,7 +201,7 @@ fun ModelsManagementBottomBar(
|
|||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = onDeleteModeClicked) {
|
||||
IconButton(onClick = { selection.toggleMode(true) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.DeleteSweep,
|
||||
contentDescription = "Delete models"
|
||||
|
|
@ -212,19 +211,19 @@ fun ModelsManagementBottomBar(
|
|||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
onClick = if (isMultiSelectionMode) onExitSelectionMode else onAddModelClicked,
|
||||
onClick = { if (selection.isActive) selection.toggleMode(false) else importing.toggleMenu(true) },
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isMultiSelectionMode) Icons.Default.Close else Icons.Default.Add,
|
||||
contentDescription = if (isMultiSelectionMode) "Exit selection mode" else "Add model"
|
||||
imageVector = if (selection.isActive) Icons.Default.Close else Icons.Default.Add,
|
||||
contentDescription = if (selection.isActive) "Exit selection mode" else "Add model"
|
||||
)
|
||||
}
|
||||
|
||||
// Add model dropdown menu
|
||||
DropdownMenu(
|
||||
expanded = showImportModelMenu,
|
||||
onDismissRequest = onImportMenuDismissed
|
||||
expanded = importing.isMenuVisible,
|
||||
onDismissRequest = { importing.toggleMenu(false) }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Import local model") },
|
||||
|
|
@ -234,7 +233,7 @@ fun ModelsManagementBottomBar(
|
|||
contentDescription = "Import a local model on the device"
|
||||
)
|
||||
},
|
||||
onClick = onImportLocalModelClicked
|
||||
onClick = importing.importFromLocal
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Download from HuggingFace") },
|
||||
|
|
@ -246,7 +245,7 @@ fun ModelsManagementBottomBar(
|
|||
tint = Color.Unspecified,
|
||||
)
|
||||
},
|
||||
onClick = onImportHuggingFaceClicked
|
||||
onClick = importing.importFromHuggingFace
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ fun ModelsManagementScreen(
|
|||
) {
|
||||
if (isMultiSelectionMode) {
|
||||
// Exit selection mode if in selection mode
|
||||
viewModel.setMultiSelectionMode(false)
|
||||
viewModel.toggleSelectionMode(false)
|
||||
} else {
|
||||
/* Ignore back press while processing model management requests */
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ fun ModelsManagementScreen(
|
|||
model = model,
|
||||
onClick = {
|
||||
if (isMultiSelectionMode) {
|
||||
viewModel.toggleModelSelection(model.id)
|
||||
viewModel.toggleModelSelectionById(model.id)
|
||||
} else {
|
||||
viewModel.viewModelDetails(model.id)
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ fun ModelsManagementScreen(
|
|||
|
||||
is Deletion.Success -> {
|
||||
LaunchedEffect(state) {
|
||||
viewModel.setMultiSelectionMode(false)
|
||||
viewModel.toggleSelectionMode(false)
|
||||
|
||||
val count = state.models.size
|
||||
onScaffoldEvent(
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ class ModelsManagementViewModel @Inject constructor(
|
|||
private val _isMultiSelectionMode = MutableStateFlow(false)
|
||||
val isMultiSelectionMode: StateFlow<Boolean> = _isMultiSelectionMode.asStateFlow()
|
||||
|
||||
fun setMultiSelectionMode(enabled: Boolean) {
|
||||
fun toggleSelectionMode(enabled: Boolean) {
|
||||
_isMultiSelectionMode.value = enabled
|
||||
if (!enabled) {
|
||||
clearSelectedModels()
|
||||
toggleAllSelection(selectAll = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ class ModelsManagementViewModel @Inject constructor(
|
|||
private val _selectedModels = MutableStateFlow<Map<String, ModelInfo>>(emptyMap())
|
||||
val selectedModels: StateFlow<Map<String, ModelInfo>> = _selectedModels.asStateFlow()
|
||||
|
||||
fun toggleModelSelection(modelId: String) {
|
||||
fun toggleModelSelectionById(modelId: String) {
|
||||
val current = _selectedModels.value.toMutableMap()
|
||||
val model = _sortedModels.value.find { it.id == modelId }
|
||||
|
||||
|
|
@ -71,12 +71,12 @@ class ModelsManagementViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun selectAllModels() {
|
||||
_selectedModels.value = _sortedModels.value.associateBy { it.id }
|
||||
}
|
||||
|
||||
fun clearSelectedModels() {
|
||||
_selectedModels.value = emptyMap()
|
||||
fun toggleAllSelection(selectAll: Boolean) {
|
||||
if (selectAll) {
|
||||
_selectedModels.value = _sortedModels.value.associateBy { it.id }
|
||||
} else {
|
||||
_selectedModels.value = emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
// UI state: sort menu
|
||||
|
|
|
|||
Loading…
Reference in New Issue