fix: иконки шторки в единой круглой рамке; кэш Задач для быстрого показа
- Шторка: КАЖДАЯ иконка теперь в белом круге с рамкой и мягкой тенью, все одного размера (глиф внутри 56% рамки) — как на мобильном сайте (было: плоские глифы без рамки, разного размера). - Задачи грузятся быстрее: TasksCache (в памяти на время процесса) — списки и задачи по спискам показываются мгновенно из кэша при повторном открытии раздела/списка, сеть обновляет в фоне. Гонки учтены: результат применяется, только если пользователь ещё на этом списке. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package ru.forbion.f7cloud.feature.tasks
|
||||
|
||||
/**
|
||||
* Лёгкий кэш в памяти на время жизни процесса: списки задач и задачи по каждому списку.
|
||||
* VM пересоздаётся при каждом открытии раздела Задач — без кэша это полный PROPFIND+REPORT
|
||||
* заново. Кэш даёт мгновенный показ, сеть обновляет в фоне.
|
||||
*/
|
||||
internal object TasksCache {
|
||||
@Volatile
|
||||
private var lists: List<TaskListItem>? = null
|
||||
private val tasksByList = java.util.concurrent.ConcurrentHashMap<String, List<TaskItem>>()
|
||||
|
||||
fun cachedLists(): List<TaskListItem>? = lists
|
||||
|
||||
fun putLists(value: List<TaskListItem>) {
|
||||
lists = value
|
||||
}
|
||||
|
||||
fun cachedTasks(listHref: String): List<TaskItem>? = tasksByList[listHref]
|
||||
|
||||
fun putTasks(listHref: String, value: List<TaskItem>) {
|
||||
tasksByList[listHref] = value
|
||||
}
|
||||
|
||||
fun invalidateTasks(listHref: String) {
|
||||
tasksByList.remove(listHref)
|
||||
}
|
||||
}
|
||||
@@ -74,10 +74,27 @@ class TasksViewModel(
|
||||
val state: StateFlow<TasksUiState> = _state.asStateFlow()
|
||||
|
||||
fun load(session: AuthSession) {
|
||||
// Мгновенно показываем кэш (списки + задачи выбранного), сеть обновляет в фоне.
|
||||
val cachedLists = TasksCache.cachedLists()
|
||||
if (cachedLists != null && cachedLists.isNotEmpty()) {
|
||||
val selected = _state.value.selectedListHref
|
||||
?: _state.value.defaultListHref
|
||||
?: cachedLists.firstOrNull()?.href
|
||||
_state.update {
|
||||
it.copy(
|
||||
loading = false,
|
||||
lists = cachedLists,
|
||||
selectedListHref = selected,
|
||||
defaultListHref = it.defaultListHref ?: cachedLists.firstOrNull()?.href,
|
||||
tasks = selected?.let { href -> TasksCache.cachedTasks(href) } ?: it.tasks,
|
||||
)
|
||||
}
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_state.update { it.copy(loading = it.lists.isEmpty(), error = null) }
|
||||
runCatching { repository.listTaskLists(session) }
|
||||
.onSuccess { lists ->
|
||||
TasksCache.putLists(lists)
|
||||
val selected = _state.value.selectedListHref
|
||||
?: _state.value.defaultListHref
|
||||
?: lists.firstOrNull()?.href
|
||||
@@ -107,6 +124,8 @@ class TasksViewModel(
|
||||
_state.update {
|
||||
it.copy(
|
||||
selectedListHref = listHref,
|
||||
// Мгновенно из кэша, если есть — иначе пусто до загрузки.
|
||||
tasks = TasksCache.cachedTasks(listHref).orEmpty(),
|
||||
searchQuery = "",
|
||||
detailTask = null,
|
||||
createInputExpanded = false,
|
||||
@@ -381,7 +400,11 @@ class TasksViewModel(
|
||||
_state.update { it.copy(loading = _state.value.tasks.isEmpty(), error = null) }
|
||||
runCatching { repository.loadTasks(session, listHref) }
|
||||
.onSuccess { tasks ->
|
||||
_state.update { it.copy(loading = false, tasks = tasks) }
|
||||
TasksCache.putTasks(listHref, tasks)
|
||||
// Обновляем список задач, только если пользователь всё ещё на этом списке.
|
||||
if (_state.value.selectedListHref == listHref) {
|
||||
_state.update { it.copy(loading = false, tasks = tasks) }
|
||||
}
|
||||
}
|
||||
.onFailure { t ->
|
||||
_state.update {
|
||||
|
||||
Reference in New Issue
Block a user