feat(ui): cascade dequeue to queued children for any parent

RemoveFromQueue previously gated cascade on PlanningPhase != None,
leaving manually-built chains stuck if their parent had no planning
phase. The handler now matches the X button's HasQueuedSubtasks gate:
queued children are unqueued and unblocked regardless of the parent's
planning phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-04-30 14:17:37 +02:00
parent d4d5a4b8e7
commit 4c92da55ad
2 changed files with 191 additions and 26 deletions

View File

@@ -535,36 +535,35 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
if (entity is null) return;
// For a planning parent the dequeue button targets queued children
// (chain-blocked or not), not the parent itself.
if (entity.PlanningPhase != PlanningPhase.None)
// Cascade to queued children when present — covers both planning parents
// (PlanningPhase != None) and bare parents that have a manually-queued
// chain. The X button's visibility is gated by the same condition
// (HasQueuedSubtasks), so the handler matches what the user can see.
var queuedChildren = await db.Tasks
.Where(t => t.ParentTaskId == row.Id && t.Status == TaskStatus.Queued)
.ToListAsync();
foreach (var c in queuedChildren)
{
var children = await db.Tasks
.Where(t => t.ParentTaskId == row.Id && t.Status == TaskStatus.Queued)
.ToListAsync();
foreach (var c in children)
{
c.Status = TaskStatus.Idle;
c.BlockedByTaskId = null;
}
await db.SaveChangesAsync();
foreach (var c in children)
{
var childRow = Items.FirstOrDefault(r => r.Id == c.Id);
if (childRow is not null)
{
childRow.Status = TaskStatus.Idle;
childRow.BlockedByTaskId = null;
}
}
row.HasQueuedSubtasks = false;
c.Status = TaskStatus.Idle;
c.BlockedByTaskId = null;
}
else
{
if (entity.Status == TaskStatus.Queued)
entity.Status = TaskStatus.Idle;
await db.SaveChangesAsync();
row.Status = TaskStatus.Idle;
await db.SaveChangesAsync();
foreach (var c in queuedChildren)
{
var childRow = Items.FirstOrDefault(r => r.Id == c.Id);
if (childRow is not null)
{
childRow.Status = TaskStatus.Idle;
childRow.BlockedByTaskId = null;
}
}
if (row.Status == TaskStatus.Queued)
row.Status = TaskStatus.Idle;
row.HasQueuedSubtasks = false;
Regroup();
UpdateSubtitle();
TasksChanged?.Invoke(this, EventArgs.Empty);