UI: address Rojin's UX feedbacks
This commit is contained in:
parent
6fb4a94cc3
commit
e067f7051b
|
|
@ -99,6 +99,7 @@ fun AppContent(
|
||||||
val engineState by mainViewModel.engineState.collectAsState()
|
val engineState by mainViewModel.engineState.collectAsState()
|
||||||
val showModelImportTooltip by mainViewModel.showModelImportTooltip.collectAsState()
|
val showModelImportTooltip by mainViewModel.showModelImportTooltip.collectAsState()
|
||||||
val showChatTooltip by mainViewModel.showChatTooltip.collectAsState()
|
val showChatTooltip by mainViewModel.showChatTooltip.collectAsState()
|
||||||
|
val showManagementTooltip by mainViewModel.showModelManagementTooltip.collectAsState()
|
||||||
|
|
||||||
// Model state
|
// Model state
|
||||||
val modelScreenUiMode by modelsViewModel.modelScreenUiMode.collectAsState()
|
val modelScreenUiMode by modelsViewModel.modelScreenUiMode.collectAsState()
|
||||||
|
|
@ -223,9 +224,11 @@ fun AppContent(
|
||||||
modelsViewModel.resetPreselection()
|
modelsViewModel.resetPreselection()
|
||||||
openDrawer()
|
openDrawer()
|
||||||
},
|
},
|
||||||
|
showTooltip = showManagementTooltip && !showChatTooltip && hasModelsInstalled,
|
||||||
showManagingToggle = !showChatTooltip && hasModelsInstalled,
|
showManagingToggle = !showChatTooltip && hasModelsInstalled,
|
||||||
onToggleManaging = {
|
onToggleManaging = {
|
||||||
if (hasModelsInstalled) {
|
if (hasModelsInstalled) {
|
||||||
|
mainViewModel.waiveModelManagementTooltip()
|
||||||
modelsViewModel.toggleMode(ModelScreenUiMode.MANAGING)
|
modelsViewModel.toggleMode(ModelScreenUiMode.MANAGING)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class AppPreferences @Inject constructor (
|
||||||
// Preference keys
|
// Preference keys
|
||||||
private val USER_HAS_IMPORTED_FIRST_MODEL = booleanPreferencesKey("user_has_imported_first_model")
|
private val USER_HAS_IMPORTED_FIRST_MODEL = booleanPreferencesKey("user_has_imported_first_model")
|
||||||
private val USER_HAS_CHATTED_WITH_MODEL = booleanPreferencesKey("user_has_chatted_with_model")
|
private val USER_HAS_CHATTED_WITH_MODEL = booleanPreferencesKey("user_has_chatted_with_model")
|
||||||
|
private val USER_HAS_NAVIGATED_TO_MANAGEMENT = booleanPreferencesKey("user_has_navigated_to_management")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,4 +65,21 @@ class AppPreferences @Inject constructor (
|
||||||
preferences[USER_HAS_CHATTED_WITH_MODEL] = done
|
preferences[USER_HAS_CHATTED_WITH_MODEL] = done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the user has navigated to model management screen.
|
||||||
|
*/
|
||||||
|
fun userHasNavigatedToManagement(): Flow<Boolean> =
|
||||||
|
context.appDataStore.data.map { preferences ->
|
||||||
|
preferences[USER_HAS_NAVIGATED_TO_MANAGEMENT] == true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the user has navigated to model management screen.
|
||||||
|
*/
|
||||||
|
suspend fun setUserHasNavigatedToManagement(done: Boolean) = withContext(Dispatchers.IO) {
|
||||||
|
context.appDataStore.edit { preferences ->
|
||||||
|
preferences[USER_HAS_NAVIGATED_TO_MANAGEMENT] = done
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ fun AppScaffold(
|
||||||
|
|
||||||
is TopBarConfig.ModelsBrowsing -> ModelsBrowsingTopBar(
|
is TopBarConfig.ModelsBrowsing -> ModelsBrowsingTopBar(
|
||||||
title = topBarconfig.title,
|
title = topBarconfig.title,
|
||||||
|
showTooltip = topBarconfig.showTooltip,
|
||||||
showManagingToggle = topBarconfig.showManagingToggle,
|
showManagingToggle = topBarconfig.showManagingToggle,
|
||||||
onToggleManaging = topBarconfig.onToggleManaging,
|
onToggleManaging = topBarconfig.onToggleManaging,
|
||||||
onNavigateBack = topBarconfig.navigationIcon.backAction,
|
onNavigateBack = topBarconfig.navigationIcon.backAction,
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ fun ModelsManagementBottomBar(
|
||||||
onDismissRequest = { importingConfig.toggleMenu(false) }
|
onDismissRequest = { importingConfig.toggleMenu(false) }
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Import a local GGUF model") },
|
text = { Text("Import a local model") },
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.FolderOpen,
|
imageVector = Icons.Default.FolderOpen,
|
||||||
|
|
@ -217,7 +217,7 @@ fun ModelsManagementBottomBar(
|
||||||
onClick = importingConfig.importFromLocal
|
onClick = importingConfig.importFromLocal
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Download from HuggingFace") },
|
text = { Text("Download a model from Hugging Face") },
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.logo_huggingface),
|
painter = painterResource(id = R.drawable.logo_huggingface),
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,16 @@ import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TooltipAnchorPosition
|
||||||
|
import androidx.compose.material3.TooltipBox
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTooltipState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -24,11 +30,23 @@ import androidx.compose.ui.unit.dp
|
||||||
@Composable
|
@Composable
|
||||||
fun ModelsBrowsingTopBar(
|
fun ModelsBrowsingTopBar(
|
||||||
title: String,
|
title: String,
|
||||||
|
showTooltip: Boolean,
|
||||||
showManagingToggle: Boolean,
|
showManagingToggle: Boolean,
|
||||||
onToggleManaging: () -> Unit,
|
onToggleManaging: () -> Unit,
|
||||||
onNavigateBack: (() -> Unit)? = null,
|
onNavigateBack: (() -> Unit)? = null,
|
||||||
onMenuOpen: (() -> Unit)? = null,
|
onMenuOpen: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
|
val tooltipState = rememberTooltipState(
|
||||||
|
initialIsVisible = showTooltip,
|
||||||
|
isPersistent = showTooltip
|
||||||
|
)
|
||||||
|
|
||||||
|
LaunchedEffect(showTooltip, showManagingToggle) {
|
||||||
|
if (showTooltip && showManagingToggle) {
|
||||||
|
tooltipState.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(title) },
|
title = { Text(title) },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
|
|
@ -54,8 +72,20 @@ fun ModelsBrowsingTopBar(
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (showManagingToggle) {
|
if (showManagingToggle) {
|
||||||
ModelManageActionToggle(onToggleManaging)
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(
|
||||||
|
TooltipAnchorPosition.Below),
|
||||||
|
state = tooltipState,
|
||||||
|
tooltip = {
|
||||||
|
PlainTooltip {
|
||||||
|
Text("Tap this button to install another model or manage your models!")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = {}
|
||||||
|
) {
|
||||||
|
ModelManageActionToggle(onToggleManaging)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ sealed class TopBarConfig {
|
||||||
data class ModelsBrowsing(
|
data class ModelsBrowsing(
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val navigationIcon: NavigationIcon,
|
override val navigationIcon: NavigationIcon,
|
||||||
|
val showTooltip: Boolean,
|
||||||
val showManagingToggle: Boolean,
|
val showManagingToggle: Boolean,
|
||||||
val onToggleManaging: () -> Unit,
|
val onToggleManaging: () -> Unit,
|
||||||
) : TopBarConfig()
|
) : TopBarConfig()
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.SegmentedButton
|
import androidx.compose.material3.SegmentedButton
|
||||||
import androidx.compose.material3.SegmentedButtonDefaults
|
import androidx.compose.material3.SegmentedButtonDefaults
|
||||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||||
|
import androidx.compose.material3.SnackbarDuration
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -250,6 +251,7 @@ fun ModelLoadingScreen(
|
||||||
if (!showedSystemPromptWarning) {
|
if (!showedSystemPromptWarning) {
|
||||||
onScaffoldEvent(ScaffoldEvent.ShowSnackbar(
|
onScaffoldEvent(ScaffoldEvent.ShowSnackbar(
|
||||||
message = "Model may not support system prompt!\nProceed with caution.",
|
message = "Model may not support system prompt!\nProceed with caution.",
|
||||||
|
duration = SnackbarDuration.Long,
|
||||||
))
|
))
|
||||||
showedSystemPromptWarning = true
|
showedSystemPromptWarning = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ fun ModelsManagementAndDeletingScreen(
|
||||||
} else if (filteredModels.isEmpty()) {
|
} else if (filteredModels.isEmpty()) {
|
||||||
// Prompt the user to import a model
|
// Prompt the user to import a model
|
||||||
val title = when (activeFiltersCount) {
|
val title = when (activeFiltersCount) {
|
||||||
0 -> "Import or download,\n have it your way"
|
0 -> "Choose a model:\nImport locally or download online"
|
||||||
1 -> "No models match\n the selected filter"
|
1 -> "No models match\n the selected filter"
|
||||||
else -> "No models match\n the selected filters"
|
else -> "No models match\n the selected filters"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ class MainViewModel @Inject constructor (
|
||||||
private val _showChatTooltip = MutableStateFlow(true)
|
private val _showChatTooltip = MutableStateFlow(true)
|
||||||
val showChatTooltip: StateFlow<Boolean> = _showChatTooltip.asStateFlow()
|
val showChatTooltip: StateFlow<Boolean> = _showChatTooltip.asStateFlow()
|
||||||
|
|
||||||
|
private val _showModelManagementTooltip = MutableStateFlow(true)
|
||||||
|
val showModelManagementTooltip: StateFlow<Boolean> = _showModelManagementTooltip.asStateFlow()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unload the current model and release the resources
|
* Unload the current model and release the resources
|
||||||
|
|
@ -47,12 +50,11 @@ class MainViewModel @Inject constructor (
|
||||||
_showChatTooltip.value = !it
|
_showChatTooltip.value = !it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
launch {
|
||||||
}
|
appPreferences.userHasNavigatedToManagement().collect {
|
||||||
|
_showModelManagementTooltip.value = !it
|
||||||
fun waiveModelImportTooltip() {
|
}
|
||||||
viewModelScope.launch {
|
}
|
||||||
appPreferences.setUserHasImportedFirstModel(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,4 +63,15 @@ class MainViewModel @Inject constructor (
|
||||||
appPreferences.setUserHasChattedWithModel(true)
|
appPreferences.setUserHasChattedWithModel(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun waiveModelImportTooltip() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
appPreferences.setUserHasImportedFirstModel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun waiveModelManagementTooltip() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
appPreferences.setUserHasNavigatedToManagement(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue