android: ResidenceViewModel.residenceTasksState derives from _allTasks

Same screen contract, but the data flows from DataManager.allTasks
through a combine(_allTasks, _currentResidenceId) into the existing
StateFlow. No per-residence network call needed; the upstream
getTasks() refresh propagates and the screen re-renders.

Eliminates the gitea#2 race window on Android — same fix as the iOS
TaskViewModel commit. Both platforms now react to _allTasks changes
without manual refresh.
This commit is contained in:
Trey t
2026-04-25 10:44:53 -05:00
parent 882801c71d
commit 1884853e4b
@@ -70,15 +70,26 @@ class ResidenceViewModel(
/** Drives the residence-scoped projections. */ /** Drives the residence-scoped projections. */
private val _selectedResidenceId = MutableStateFlow<Int?>(null) private val _selectedResidenceId = MutableStateFlow<Int?>(null)
/// Residence-scoped kanban derived from `DataManager.allTasks` filtered
/// by `_selectedResidenceId`. Single source of truth — eliminates the
/// gitea#2 race window where the per-residence cache slot could be
/// empty while `_allTasks` was populated. The per-residence cache
/// (`tasksByResidence`) was deleted in cec521b.
val residenceTasksState: StateFlow<ApiResult<TaskColumnsResponse>> = val residenceTasksState: StateFlow<ApiResult<TaskColumnsResponse>> =
combine(_selectedResidenceId, dataManager.tasksByResidence) { id, map -> combine(_selectedResidenceId, DataManager.allTasks) { id, all ->
if (id == null) ApiResult.Idle when {
else map[id]?.let { ApiResult.Success(it) } ?: ApiResult.Idle id == null -> ApiResult.Idle
all == null -> ApiResult.Loading
else -> {
val filtered = DataManager.getTasksForResidence(id)
if (filtered != null) ApiResult.Success(filtered) else ApiResult.Loading
}
}
}.stateIn( }.stateIn(
viewModelScope, viewModelScope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
_selectedResidenceId.value?.let { id -> _selectedResidenceId.value?.let { id ->
dataManager.tasksByResidence.value[id]?.let { ApiResult.Success(it) } DataManager.getTasksForResidence(id)?.let { ApiResult.Success(it) }
} ?: ApiResult.Idle, } ?: ApiResult.Idle,
) )