fix(ui): surface worker-offline failures when deleting a task
DeleteTaskAsync had no IsConnected guard, unlike every other worker-dependent command in DetailsIslandViewModel, so an offline delete was a silent no-op. WorkerClient.DeleteTaskAsync also only caught HubException, letting the InvalidOperationException thrown by an inactive hub connection escape into the unobserved command task and vanish. Gate DeleteTaskCommand behind CanDeleteTask (Task != null && IsConnected), re-evaluate it on connection-state changes, widen WorkerClient to catch the connection-inactive case too, and wrap the ViewModel's call in try/catch as a second line of defense against a race between the guard and the call.
This commit is contained in:
@@ -355,6 +355,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
ContinueCommand.NotifyCanExecuteChanged();
|
||||
SendRoadblockReplyCommand.NotifyCanExecuteChanged();
|
||||
CancelReviewCommand.NotifyCanExecuteChanged();
|
||||
DeleteTaskCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
};
|
||||
_worker.PropertyChanged += _workerPropertyChangedHandler;
|
||||
@@ -963,7 +964,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
await repo.UpdateAsync(entity);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
[RelayCommand(CanExecute = nameof(CanDeleteTask))]
|
||||
private async System.Threading.Tasks.Task DeleteTaskAsync()
|
||||
{
|
||||
if (Task == null) return;
|
||||
@@ -976,7 +977,20 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
// Routed through the worker (mirrors the MCP delete_task tool) so a deleted child
|
||||
// correctly advances a WaitingForChildren parent — a direct-repo delete from here used
|
||||
// to bypass TaskStateService.TryAdvanceParentAsync and could wedge the parent forever.
|
||||
var (deleted, error) = await _worker.DeleteTaskAsync(row.Id);
|
||||
bool deleted;
|
||||
string? error;
|
||||
try
|
||||
{
|
||||
(deleted, error) = await _worker.DeleteTaskAsync(row.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Belt and braces: the connection can drop between the CanExecute check and
|
||||
// this call, so a stray throw here must surface, not vanish silently.
|
||||
if (ShowErrorAsync != null)
|
||||
await ShowErrorAsync(ex.Message);
|
||||
return;
|
||||
}
|
||||
if (!deleted)
|
||||
{
|
||||
if (ShowErrorAsync != null)
|
||||
@@ -988,6 +1002,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
CloseDetail?.Invoke();
|
||||
}
|
||||
|
||||
private bool CanDeleteTask() => Task != null && _worker.IsConnected;
|
||||
|
||||
[RelayCommand]
|
||||
private async System.Threading.Tasks.Task CommitSubtaskEditAsync(SubtaskRowViewModel? row)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user