From dea8eed18421db29289bdb767f4b281f8a779186 Mon Sep 17 00:00:00 2001 From: Trey t Date: Sat, 25 Apr 2026 10:41:06 -0500 Subject: [PATCH] refactor: getTasksByResidence is now a thin filter over _allTasks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was 3 fallback paths (per-residence cache → filter from allTasks → network). Now: ensure _allTasks fresh, return filter. The per-residence cache becomes write-only by this path, scheduled for deletion in the next commit. Eliminates a class of bugs where the per-residence cache slot could be missing while _allTasks was stale — the old Path 1+2 would either return stale data or skip and hit the API redundantly. --- .../com/tt/honeyDue/network/APILayer.kt | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/network/APILayer.kt b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/network/APILayer.kt index 8dc1889..552c6b3 100644 --- a/composeApp/src/commonMain/kotlin/com/tt/honeyDue/network/APILayer.kt +++ b/composeApp/src/commonMain/kotlin/com/tt/honeyDue/network/APILayer.kt @@ -615,36 +615,22 @@ object APILayer { return result } + /** + * Returns kanban data for a single residence. Single source of truth + * is `_allTasks`; this function ensures it's fresh, then filters. + * + * Replaces the previous 3-path implementation (per-residence cache → + * filter from allTasks → API) that produced inconsistent results + * when the per-residence cache slot was empty but `_allTasks` was + * stale. Phase 3 deletes the per-residence cache entirely. + */ suspend fun getTasksByResidence(residenceId: Int, forceRefresh: Boolean = false): ApiResult { - // 1. Check residence-specific cache first - if (!forceRefresh && DataManager.isCacheValid(DataManager.tasksByResidenceCacheTime[residenceId] ?: 0L)) { - val cached = DataManager.tasksByResidence.value[residenceId] - if (cached != null) { - return ApiResult.Success(cached) - } - } + val allTasksResult = getTasks(forceRefresh = forceRefresh) + if (allTasksResult is ApiResult.Error) return allTasksResult - // 2. Try filtering from allTasks cache before hitting API (optimization) - // This avoids a redundant API call when we already have all tasks loaded - if (!forceRefresh && DataManager.isCacheValid(DataManager.tasksCacheTime)) { - val filtered = DataManager.getTasksForResidence(residenceId) - if (filtered != null) { - // Cache the filtered result for future use - DataManager.setTasksForResidence(residenceId, filtered) - return ApiResult.Success(filtered) - } - } - - // 3. Fallback: Fetch from API - val token = getToken() ?: return ApiResult.Error("Not authenticated", 401) - val result = taskApi.getTasksByResidence(token, residenceId) - - // Update DataManager on success - if (result is ApiResult.Success) { - DataManager.setTasksForResidence(residenceId, result.data) - } - - return result + val filtered = DataManager.getTasksForResidence(residenceId) + ?: return ApiResult.Error("Tasks unavailable", 0) + return ApiResult.Success(filtered) } suspend fun createTask(request: TaskCreateRequest): ApiResult {