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

SmartListMode (HIDDEN/VISIBLE/AUTO) по семантике веб server-show 0/1/2.
TasksSmartLists.counts()/visibleKeys() — сводка и разрешение видимых коллекций.
25 юнит-тестов зелёные. Далее: агрегация в VM + вывод в TasksListNavView.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
b-mob-tasks
2026-07-13 05:54:53 +00:00
parent 286722d953
commit e405dad8fa
2 changed files with 96 additions and 22 deletions
@@ -29,23 +29,36 @@ enum class SmartListKey(val title: String, val icon: String) {
COMPLETED("Завершенные", "check-done-gray.svg"),
}
/** Какие умные списки показывать в панели (шит «Параметры задач»). По умолчанию — все. */
/**
* Режим видимости умной коллекции в панели — как в веб-Tasks (server `show` 0/1/2):
* [HIDDEN] всегда скрыта · [VISIBLE] всегда видима · [AUTO] видима только при count > 0.
*/
enum class SmartListMode { HIDDEN, VISIBLE, AUTO }
/**
* Настройка видимости умных коллекций (шит «Параметры задач»). Хранит только НЕ-дефолтные
* режимы — для компактной сериализации в SharedPreferences. Умолчание — [DEFAULT_MODE].
*/
data class SmartListVisibility(
private val hidden: Set<SmartListKey> = emptySet(),
private val modes: Map<SmartListKey, SmartListMode> = emptyMap(),
) {
fun isVisible(key: SmartListKey): Boolean = key !in hidden
fun mode(key: SmartListKey): SmartListMode = modes[key] ?: DEFAULT_MODE
fun withVisible(key: SmartListKey, visible: Boolean): SmartListVisibility =
SmartListVisibility(if (visible) hidden - key else hidden + key)
fun withMode(key: SmartListKey, mode: SmartListMode): SmartListVisibility =
SmartListVisibility(
if (mode == DEFAULT_MODE) modes - key else modes + (key to mode),
)
/** Ключи скрытых списков — для сериализации в SharedPreferences. */
fun hiddenKeys(): Set<SmartListKey> = hidden
/** Только заданные вручную (не-дефолтные) режимы — для персиста. */
fun explicitModes(): Map<SmartListKey, SmartListMode> = modes
companion object {
val ALL_VISIBLE = SmartListVisibility()
/** Умолчание: AUTO (показывать при непустом счётчике) — сверить с сервером live. */
val DEFAULT_MODE = SmartListMode.AUTO
val DEFAULT = SmartListVisibility()
fun of(hidden: Collection<SmartListKey>): SmartListVisibility =
SmartListVisibility(hidden.toSet())
fun of(modes: Map<SmartListKey, SmartListMode>): SmartListVisibility =
SmartListVisibility(modes.filterValues { it != DEFAULT_MODE })
}
}
@@ -74,6 +87,25 @@ object TasksSmartLists {
fun count(key: SmartListKey, tasks: List<TaskItem>, today: LocalDate): Int =
tasks.count { matches(key, it, today) }
/** Счётчики всех коллекций сразу (по агрегату задач всех списков). */
fun counts(tasks: List<TaskItem>, today: LocalDate): Map<SmartListKey, Int> =
SmartListKey.entries.associateWith { key -> count(key, tasks, today) }
/**
* Коллекции для показа в панели (в порядке enum) с учётом режима видимости и счётчиков:
* VISIBLE — всегда, AUTO — при count > 0, HIDDEN — никогда.
*/
fun visibleKeys(
visibility: SmartListVisibility,
counts: Map<SmartListKey, Int>,
): List<SmartListKey> = SmartListKey.entries.filter { key ->
when (visibility.mode(key)) {
SmartListMode.VISIBLE -> true
SmartListMode.HIDDEN -> false
SmartListMode.AUTO -> (counts[key] ?: 0) > 0
}
}
/** Уже стартовала: нет start, либо start сегодня/в прошлом. */
private fun TaskItem.hasStarted(today: LocalDate): Boolean {
val start = startLocalDate() ?: return true