fix(claude-do): merge Make the merge modal non-modal so a running merge no longer
ClaudeDo-Task: b2b4c74fc265435b9f083f56d0e47546
This commit is contained in:
@@ -165,12 +165,12 @@ public partial class DetailsIslandView : UserControl
|
|||||||
return System.Threading.Tasks.Task.CompletedTask;
|
return System.Threading.Tasks.Task.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.Merge.ShowMergeModal = async (mergeVm) =>
|
// Non-modal so the main window stays usable while the merge + verify runs; the task
|
||||||
|
// completes on close so the diff viewer can still react to the outcome.
|
||||||
|
vm.Merge.ShowMergeModal = (mergeVm) =>
|
||||||
{
|
{
|
||||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||||
if (owner == null) return;
|
return owner == null ? System.Threading.Tasks.Task.CompletedTask : MergeModalView.ShowAsync(owner, mergeVm);
|
||||||
var modal = new MergeModalView { DataContext = mergeVm };
|
|
||||||
await modal.ShowDialog(owner);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ namespace ClaudeDo.Ui.Views.Modals;
|
|||||||
|
|
||||||
public partial class MergeModalView : Window
|
public partial class MergeModalView : Window
|
||||||
{
|
{
|
||||||
|
/// Open merge windows by task id — the guard against starting the same merge twice.
|
||||||
|
private static readonly Dictionary<string, MergeModalView> OpenWindows = new();
|
||||||
|
|
||||||
|
private readonly TaskCompletionSource _closed = new();
|
||||||
|
|
||||||
public MergeModalView()
|
public MergeModalView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -17,5 +22,26 @@ public partial class MergeModalView : Window
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows the merge window non-modally so the owner stays usable while the merge (and its verify
|
||||||
|
/// command) runs. Completes when the window closes; a second call for a task whose window is
|
||||||
|
/// already open activates it and waits on that one instead of opening another.
|
||||||
|
public static Task ShowAsync(Window owner, MergeModalViewModel vm)
|
||||||
|
{
|
||||||
|
if (OpenWindows.TryGetValue(vm.TaskId, out var existing))
|
||||||
|
{
|
||||||
|
if (existing.WindowState == WindowState.Minimized)
|
||||||
|
existing.WindowState = WindowState.Normal;
|
||||||
|
existing.Activate();
|
||||||
|
return existing._closed.Task;
|
||||||
|
}
|
||||||
|
var window = new MergeModalView { DataContext = vm };
|
||||||
|
OpenWindows[vm.TaskId] = window;
|
||||||
|
window.Closed += (_, _) =>
|
||||||
|
{
|
||||||
|
OpenWindows.Remove(vm.TaskId);
|
||||||
|
window._closed.TrySetResult();
|
||||||
|
};
|
||||||
|
window.Show(owner);
|
||||||
|
return window._closed.Task;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,11 +149,7 @@ public sealed class WindowDialogService : IDialogService
|
|||||||
if (Shell is { } shell)
|
if (Shell is { } shell)
|
||||||
{
|
{
|
||||||
vm.ResolveMergeVm = shell.ResolveMergeVm;
|
vm.ResolveMergeVm = shell.ResolveMergeVm;
|
||||||
vm.ShowMergeAction = async mergeVm =>
|
vm.ShowMergeAction = mergeVm => MergeModalView.ShowAsync(_owner, mergeVm);
|
||||||
{
|
|
||||||
var mergeDlg = new MergeModalView { DataContext = mergeVm };
|
|
||||||
await mergeDlg.ShowDialog(_owner);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
await dlg.ShowDialog(_owner);
|
await dlg.ShowDialog(_owner);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -335,4 +335,30 @@ public class DiffViewerViewModelTests
|
|||||||
Assert.Contains("planning task not found", vm.CombinedWarning);
|
Assert.Contains("planning task not found", vm.CombinedWarning);
|
||||||
Assert.Equal("", vm.DisplayedDiff);
|
Assert.Equal("", vm.DisplayedDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Merge button: the modal is non-modal, the command spans its lifetime ──
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Merge_WhileMergeWindowOpen_DoesNotStartSecondMerge()
|
||||||
|
{
|
||||||
|
var worker = new FakePlanningWorker();
|
||||||
|
var vm = new DiffViewerViewModel(null!, worker, TestSettings());
|
||||||
|
vm.ConfigureWorktree("/wt", "main", "task-1", "Title");
|
||||||
|
var windowClosed = new TaskCompletionSource();
|
||||||
|
var resolved = 0;
|
||||||
|
var shown = 0;
|
||||||
|
vm.ResolveMergeVm = () => { resolved++; return new MergeModalViewModel(worker, new MergeCoordinator()); };
|
||||||
|
vm.ShowMergeModal = _ => { shown++; return windowClosed.Task; };
|
||||||
|
|
||||||
|
var first = vm.MergeCommand.ExecuteAsync(null);
|
||||||
|
while (shown == 0) await Task.Delay(10);
|
||||||
|
|
||||||
|
Assert.False(vm.MergeCommand.CanExecute(null));
|
||||||
|
Assert.Equal(1, resolved);
|
||||||
|
Assert.Equal(1, shown);
|
||||||
|
|
||||||
|
windowClosed.SetResult();
|
||||||
|
await first;
|
||||||
|
Assert.True(vm.MergeCommand.CanExecute(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user