feat(ui): route detail-pane review actions through OperationStatus

Approve, Submit, Reject, and Park each get their own OperationStatus so the
button locks and shows an OperationIndicator for the duration of the call;
Approve additionally scopes a MergeProgressEvent subscription to its own
task id to sharpen the label from "merging" to "verifying" mid-flight.
MergeSectionViewModel's preview refresh gets the same treatment for display
only, since it has no command button to gate.
This commit is contained in:
Mika Kuns
2026-08-12 09:42:15 +02:00
parent d3abd4b88b
commit bea85a9ee5
4 changed files with 277 additions and 18 deletions
@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Modals;
using Microsoft.Extensions.DependencyInjection;
@@ -27,6 +28,10 @@ public sealed partial class MergeSectionViewModel : ViewModelBase
[ObservableProperty] private ObservableCollection<string> _mergeTargetBranches = new();
[ObservableProperty] private string? _selectedMergeTarget;
// No CanExecute to gate here — RefreshMergePreviewAsync is triggered from the Bind path,
// not a [RelayCommand], so PreviewOp only drives the indicator, never locks a button.
public OperationStatus PreviewOp { get; } = new();
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowMergePreviewMuted))]
private string _mergePreviewText = "";
@@ -118,6 +123,7 @@ public sealed partial class MergeSectionViewModel : ViewModelBase
}
var capturedTaskId = TaskId;
var capturedTarget = SelectedMergeTarget;
using var op = PreviewOp.Begin(Loc.T("ops.review.previewing"));
var dto = await _worker.PreviewMergeAsync(capturedTaskId, capturedTarget ?? "");
if (TaskId != capturedTaskId || SelectedMergeTarget != capturedTarget) return;
var (text, clean, conflict) = MergePreviewPresenter.Describe(dto);