fix: DataManager.updateTask seeds _allTasks when cache is empty (gitea#2)
Closes the silent no-op when _allTasks is null on first launch (the onboarding bulkCreateTasks path). The function now upserts: builds an empty kanban shell with the standard column names if needed and places the task in its target column. Unknown column names append a new column at the end so the task is always reachable. Also drops the second branch that conditionally wrote to _tasksByResidence — that cache is being deleted in Phase 3 and updateTask should not maintain it any more. The Phase 1 unit tests now pass; the Phase 2 force-refresh in the next commit replaces the placeholder column metadata (display names, colors, icons) with authoritative server values.
This commit is contained in:
@@ -480,45 +480,60 @@ object DataManager {
|
|||||||
* Also refreshes the summary from the updated kanban data.
|
* Also refreshes the summary from the updated kanban data.
|
||||||
*/
|
*/
|
||||||
fun updateTask(task: TaskResponse) {
|
fun updateTask(task: TaskResponse) {
|
||||||
// Update in allTasks
|
val targetColumn = task.kanbanColumn ?: "upcoming_tasks"
|
||||||
_allTasks.value?.let { current ->
|
|
||||||
val targetColumn = task.kanbanColumn ?: "upcoming_tasks"
|
|
||||||
val newColumns = current.columns.map { column ->
|
|
||||||
// Remove task from this column if present
|
|
||||||
val filteredTasks = column.tasks.filter { it.id != task.id }
|
|
||||||
// Add task if this is the target column
|
|
||||||
val updatedTasks = if (column.name == targetColumn) {
|
|
||||||
filteredTasks + task
|
|
||||||
} else {
|
|
||||||
filteredTasks
|
|
||||||
}
|
|
||||||
column.copy(tasks = updatedTasks, count = updatedTasks.size)
|
|
||||||
}
|
|
||||||
_allTasks.value = current.copy(columns = newColumns)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update in tasksByResidence if this task's residence is cached
|
// Upsert into _allTasks. Crucially, when _allTasks is null (fresh
|
||||||
task.residenceId?.let { residenceId ->
|
// launch, kanban never fetched — the gitea#2 bug scenario), seed
|
||||||
_tasksByResidence.value[residenceId]?.let { current ->
|
// an empty kanban shell so the new task isn't silently dropped.
|
||||||
val targetColumn = task.kanbanColumn ?: "upcoming_tasks"
|
// The Phase 2 force-refresh after bulkCreateTasks/createTask will
|
||||||
val newColumns = current.columns.map { column ->
|
// replace this shell with authoritative server data shortly.
|
||||||
val filteredTasks = column.tasks.filter { it.id != task.id }
|
val current = _allTasks.value ?: emptyKanbanShell()
|
||||||
val updatedTasks = if (column.name == targetColumn) {
|
val columnsWithTarget = if (current.columns.any { it.name == targetColumn }) {
|
||||||
filteredTasks + task
|
current.columns
|
||||||
} else {
|
} else {
|
||||||
filteredTasks
|
// Server returned a kanban_column the client doesn't know about
|
||||||
}
|
// yet — append it so the task is still reachable.
|
||||||
column.copy(tasks = updatedTasks, count = updatedTasks.size)
|
current.columns + emptyColumn(targetColumn)
|
||||||
}
|
|
||||||
_tasksByResidence.value = _tasksByResidence.value + (residenceId to current.copy(columns = newColumns))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
val newColumns = columnsWithTarget.map { column ->
|
||||||
|
val filteredTasks = column.tasks.filter { it.id != task.id }
|
||||||
|
val updatedTasks = if (column.name == targetColumn) filteredTasks + task else filteredTasks
|
||||||
|
column.copy(tasks = updatedTasks, count = updatedTasks.size)
|
||||||
|
}
|
||||||
|
_allTasks.value = current.copy(columns = newColumns)
|
||||||
|
|
||||||
// Refresh summary from updated kanban data (API no longer returns summaries for CRUD)
|
// Refresh summary from updated kanban data (API no longer returns summaries for CRUD)
|
||||||
refreshSummaryFromKanban()
|
refreshSummaryFromKanban()
|
||||||
persistToDisk()
|
persistToDisk()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default kanban skeleton used when `_allTasks` was never populated.
|
||||||
|
/// Display metadata is intentionally placeholder — the Phase 2 force-refresh
|
||||||
|
/// in `APILayer.bulkCreateTasks` / `createTask` replaces these shortly with
|
||||||
|
/// authoritative server values. The `name` field is the contract — every
|
||||||
|
/// observer keys off it.
|
||||||
|
private fun emptyKanbanShell(): TaskColumnsResponse = TaskColumnsResponse(
|
||||||
|
columns = listOf(
|
||||||
|
emptyColumn("overdue_tasks"),
|
||||||
|
emptyColumn("due_soon_tasks"),
|
||||||
|
emptyColumn("in_progress_tasks"),
|
||||||
|
emptyColumn("upcoming_tasks"),
|
||||||
|
emptyColumn("completed_tasks")
|
||||||
|
),
|
||||||
|
daysThreshold = 30,
|
||||||
|
residenceId = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun emptyColumn(name: String): TaskColumn = TaskColumn(
|
||||||
|
name = name,
|
||||||
|
displayName = "",
|
||||||
|
buttonTypes = emptyList(),
|
||||||
|
icons = emptyMap(),
|
||||||
|
color = "",
|
||||||
|
tasks = emptyList(),
|
||||||
|
count = 0
|
||||||
|
)
|
||||||
|
|
||||||
fun removeTask(taskId: Int) {
|
fun removeTask(taskId: Int) {
|
||||||
// Remove from allTasks
|
// Remove from allTasks
|
||||||
_allTasks.value?.let { current ->
|
_allTasks.value?.let { current ->
|
||||||
|
|||||||
Reference in New Issue
Block a user