feat(ui): add "move to list" to the task row context menu

Reuses the existing drag-drop move (MoveTaskToListAsync) so the same
running/worktree guards apply — only ListId changes.

Also count WaitingForReview tasks (not Done-with-a-branch) for the
review portion of the status pill, and localize the pill and open-task
count strings that were still hardcoded English.
This commit is contained in:
mika kuns
2026-09-02 10:00:21 +02:00
parent c347e4fc5f
commit 80fe188a48
4 changed files with 34 additions and 5 deletions
@@ -806,14 +806,16 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
var now = DateTime.Now;
var open = Items.Count(i => !i.Done);
var running = Items.Count(i => i.Status == TaskStatus.Running);
var review = Items.Count(i => i.Status == TaskStatus.Done && i.Branch != null);
var review = Items.Count(i => i.Status == TaskStatus.WaitingForReview);
// The notes list holds no tasks — an "0 open tasks" line under its header is just noise.
Subtitle = IsNotesList ? "" : open == 1 ? "1 open task" : $"{open} open tasks";
Subtitle = IsNotesList ? ""
: open == 1 ? Loc.T("vm.tasksIsland.openTaskOne")
: Loc.T("vm.tasksIsland.openTasks", open);
if (running > 0 || review > 0)
{
StatusPill = $"{running} running · {review} review";
StatusPill = Loc.T("vm.tasksIsland.statusPill", running, review);
HasStatusPill = true;
}
else
@@ -6,6 +6,7 @@ using Avalonia.Interactivity;
using Avalonia.VisualTree;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.ViewModels;
using ClaudeDo.Ui.ViewModels.Islands;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
@@ -97,6 +98,23 @@ public partial class TaskRowView : UserControl
schedule.Items.Add(MakeItem("tasks.ctxRemoveFromMyDay", OnRemoveFromMyDayClick, row.IsMyDay));
menu.Items.Add(schedule);
// "Move to list" — user lists only, current list excluded. Reuses the drag-drop move
// (MoveTaskToListAsync), so nothing but ListId changes and the same running/worktree
// guards apply. Hidden entirely when there is nowhere to move to.
var targets = (TopLevel.GetTopLevel(this)?.DataContext as IslandsShellViewModel)?.Lists?.UserLists
.Where(l => l.Id != vm.CurrentListId).ToList() ?? [];
if (targets.Count > 0)
{
var moveTo = new MenuItem { Header = Loc.T("tasks.ctxMoveToList"), DataContext = row };
foreach (var target in targets)
{
var item = new MenuItem { Header = target.Name, DataContext = row, Tag = target };
item.Click += OnMoveToListClick;
moveTo.Items.Add(item);
}
menu.Items.Add(moveTo);
}
menu.Items.Add(new Separator());
menu.Items.Add(MakeItem("tasks.ctxDeleteTask", OnDeleteTaskClick, reason: row.DeleteDisabledReason));
@@ -152,6 +170,13 @@ public partial class TaskRowView : UserControl
await vm.RemoveFromMyDayCommand.ExecuteAsync(row);
}
private async void OnMoveToListClick(object? sender, RoutedEventArgs e)
{
if (sender is not MenuItem { Tag: ListNavItemViewModel target }) return;
if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
await vm.MoveTaskToListAsync(row, target);
}
private async void OnDeleteTaskClick(object? sender, RoutedEventArgs e)
{
if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)