feat(tasks): P1 завершён — 3-state видимость коллекций в настройках + персист

TasksPrefsStore (SharedPreferences) хранит режим Скрыта/Видима/Авто по коллекции.
TasksViewModel через Factory(context), setSmartMode персистит; шит настроек 3-state
от VM (убраны фиктивные локальные remember). Панель реагирует через visibleKeys.
Гейт compile+detekt+test зелёный.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
b-mob-tasks
2026-07-13 06:12:17 +00:00
parent cfeb9b3b79
commit bd56c4bc7b
4 changed files with 71 additions and 35 deletions
@@ -1016,17 +1016,12 @@ fun TasksSettingsSheet(
lists: List<TaskListItem>,
defaultListHref: String?,
onDefaultListChange: (String?) -> Unit,
visibility: SmartListVisibility,
onSmartModeChange: (SmartListKey, SmartListMode) -> Unit,
onDismiss: () -> Unit,
onSwipeBack: () -> Unit = onDismiss,
) {
if (!visible) return
val visibilityOptions = listOf("Показывать", "Скрывать")
var importantVisible by remember { mutableStateOf(visibilityOptions.first()) }
var todayVisible by remember { mutableStateOf(visibilityOptions.first()) }
var weekVisible by remember { mutableStateOf(visibilityOptions.first()) }
var allVisible by remember { mutableStateOf(visibilityOptions.first()) }
var currentVisible by remember { mutableStateOf(visibilityOptions.first()) }
var completedVisible by remember { mutableStateOf(visibilityOptions.first()) }
var defaultMenuOpen by remember { mutableStateOf(false) }
Dialog(
@@ -1099,23 +1094,15 @@ fun TasksSettingsSheet(
HorizontalDivider(color = F7Colors.Border)
SmartListSettingRow(serverUrl, "important-gray.svg", "Важное", importantVisible) {
importantVisible = it
}
SmartListSettingRow(serverUrl, "calendar-today-gray.svg", "Сегодня", todayVisible) {
todayVisible = it
}
SmartListSettingRow(serverUrl, "calendar-nextweek-gray.svg", "На неделе", weekVisible) {
weekVisible = it
}
SmartListSettingRow(serverUrl, "calendar-icon-gray.svg", "Все", allVisible) {
allVisible = it
}
SmartListSettingRow(serverUrl, "current-gray.svg", "Текущие", currentVisible) {
currentVisible = it
}
SmartListSettingRow(serverUrl, "check-done-gray.svg", "Завершенные", completedVisible) {
completedVisible = it
Text("Видимость умных списков", fontSize = 14.sp, fontWeight = FontWeight.Medium)
SmartListKey.entries.forEach { key ->
SmartListSettingRow(
serverUrl = serverUrl,
iconPath = key.icon,
title = key.title,
mode = visibility.mode(key),
onModeChange = { onSmartModeChange(key, it) },
)
}
}
}
@@ -1127,11 +1114,16 @@ private fun SmartListSettingRow(
serverUrl: String,
iconPath: String,
title: String,
selected: String,
onSelect: (String) -> Unit,
mode: SmartListMode,
onModeChange: (SmartListMode) -> Unit,
) {
var menuOpen by remember { mutableStateOf(false) }
val options = listOf("Показывать", "Скрывать")
val options = listOf(
SmartListMode.VISIBLE to "Показывать",
SmartListMode.HIDDEN to "Скрывать",
SmartListMode.AUTO to "Автоматически",
)
val currentLabel = options.firstOrNull { it.first == mode }?.second ?: "Автоматически"
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = tasksAsset(serverUrl, iconPath), contentDescription = null, modifier = Modifier.size(18.dp))
@@ -1149,7 +1141,7 @@ private fun SmartListSettingRow(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(selected)
Text(currentLabel)
AsyncImage(
model = tasksAsset(serverUrl, "chevron-down-16.svg"),
contentDescription = null,
@@ -1157,11 +1149,11 @@ private fun SmartListSettingRow(
)
}
DropdownMenu(expanded = menuOpen, onDismissRequest = { menuOpen = false }) {
options.forEach { option ->
options.forEach { (optionMode, label) ->
DropdownMenuItem(
text = { Text(option) },
text = { Text(label) },
onClick = {
onSelect(option)
onModeChange(optionMode)
menuOpen = false
},
)
@@ -0,0 +1,29 @@
package ru.forbion.f7cloud.feature.tasks
import android.content.Context
/**
* Персист настроек Задач в SharedPreferences (по образцу `CalendarPrefsStore`).
* Сейчас — режим видимости умных коллекций (3-state), переживает перезапуск.
*/
class TasksPrefsStore(context: Context) {
private val prefs = context.applicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
fun getVisibility(): SmartListVisibility {
val modes = SmartListKey.entries.mapNotNull { key ->
val raw = prefs.getString(keyMode(key), null) ?: return@mapNotNull null
runCatching { SmartListMode.valueOf(raw) }.getOrNull()?.let { key to it }
}.toMap()
return SmartListVisibility.of(modes)
}
fun setMode(key: SmartListKey, mode: SmartListMode) {
prefs.edit().putString(keyMode(key), mode.name).apply()
}
companion object {
private const val PREFS = "f7_tasks_prefs"
private fun keyMode(key: SmartListKey) = "smart_mode::${key.name}"
}
}
@@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -22,6 +22,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import ru.forbion.f7cloud.core.auth.AuthSession
@@ -38,7 +39,8 @@ fun TasksScreen(
onOpenListConsumed: () -> Unit = {},
onUnauthorized: () -> Unit = {},
) {
val vm: TasksViewModel = viewModel()
val context = LocalContext.current
val vm: TasksViewModel = viewModel(factory = TasksViewModel.Factory(context))
val state by vm.state.collectAsState()
// Родитель для создаваемой подзадачи (открывает диалог ввода названия).
var subtaskParent by remember { mutableStateOf<TaskItem?>(null) }
@@ -156,6 +158,8 @@ fun TasksScreen(
lists = state.lists,
defaultListHref = state.defaultListHref,
onDefaultListChange = vm::setDefaultList,
visibility = state.smartVisibility,
onSmartModeChange = vm::setSmartMode,
onDismiss = vm::closeSettings,
onSwipeBack = vm::closeSettings,
)
@@ -1,6 +1,8 @@
package ru.forbion.f7cloud.feature.tasks
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@@ -88,9 +90,11 @@ data class TasksUiState(
}
class TasksViewModel(
context: Context,
private val repository: TasksRepository = TasksRepository(),
) : ViewModel() {
private val _state = MutableStateFlow(TasksUiState())
private val prefsStore = TasksPrefsStore(context.applicationContext)
private val _state = MutableStateFlow(TasksUiState(smartVisibility = prefsStore.getVisibility()))
val state: StateFlow<TasksUiState> = _state.asStateFlow()
fun load(session: AuthSession) {
@@ -174,8 +178,9 @@ class TasksViewModel(
loadAllTasks(session)
}
/** Сменить режим видимости умной коллекции (шит настроек). */
/** Сменить режим видимости умной коллекции (шит настроек) + персист. */
fun setSmartMode(key: SmartListKey, mode: SmartListMode) {
prefsStore.setMode(key, mode)
_state.update { it.copy(smartVisibility = it.smartVisibility.withMode(key, mode)) }
}
@@ -503,4 +508,10 @@ class TasksViewModel(
}
}
}
class Factory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T =
TasksViewModel(context.applicationContext) as T
}
}