feat(ui): add MergeModalViewModel
This commit is contained in:
117
src/ClaudeDo.Ui/ViewModels/Modals/MergeModalViewModel.cs
Normal file
117
src/ClaudeDo.Ui/ViewModels/Modals/MergeModalViewModel.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace ClaudeDo.Ui.ViewModels.Modals;
|
||||
|
||||
public sealed partial class MergeModalViewModel : ViewModelBase
|
||||
{
|
||||
private readonly WorkerClient _worker;
|
||||
|
||||
public string TaskId { get; set; } = "";
|
||||
public string TaskTitle { get; set; } = "";
|
||||
|
||||
public ObservableCollection<string> Branches { get; } = new();
|
||||
|
||||
[ObservableProperty] private string? _selectedBranch;
|
||||
[ObservableProperty] private bool _removeWorktree = true;
|
||||
[ObservableProperty] private string _commitMessage = "";
|
||||
|
||||
[ObservableProperty] private bool _isBusy;
|
||||
[ObservableProperty] private string? _errorMessage;
|
||||
[ObservableProperty] private string? _warningMessage;
|
||||
[ObservableProperty] private string? _successMessage;
|
||||
[ObservableProperty] private bool _hasConflict;
|
||||
[ObservableProperty] private IReadOnlyList<string> _conflictFiles = Array.Empty<string>();
|
||||
|
||||
public Action? CloseAction { get; set; }
|
||||
|
||||
public MergeModalViewModel(WorkerClient worker)
|
||||
{
|
||||
_worker = worker;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync(string taskId, string taskTitle)
|
||||
{
|
||||
TaskId = taskId;
|
||||
TaskTitle = taskTitle;
|
||||
CommitMessage = $"Merge task: {taskTitle}";
|
||||
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
var targets = await _worker.GetMergeTargetsAsync(taskId);
|
||||
Branches.Clear();
|
||||
if (targets is null)
|
||||
{
|
||||
ErrorMessage = "Worker offline — cannot list branches.";
|
||||
return;
|
||||
}
|
||||
foreach (var b in targets.LocalBranches) Branches.Add(b);
|
||||
SelectedBranch = Branches.Contains(targets.DefaultBranch)
|
||||
? targets.DefaultBranch
|
||||
: Branches.FirstOrDefault();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Failed to load branches: {ex.Message}";
|
||||
}
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
|
||||
private bool CanSubmit() =>
|
||||
!IsBusy && !HasConflict && !string.IsNullOrWhiteSpace(SelectedBranch);
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanSubmit))]
|
||||
private async Task SubmitAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SelectedBranch)) return;
|
||||
IsBusy = true;
|
||||
ErrorMessage = null;
|
||||
WarningMessage = null;
|
||||
SuccessMessage = null;
|
||||
try
|
||||
{
|
||||
var result = await _worker.MergeTaskAsync(
|
||||
TaskId, SelectedBranch!, RemoveWorktree, CommitMessage);
|
||||
|
||||
switch (result.Status)
|
||||
{
|
||||
case "merged":
|
||||
SuccessMessage = result.ErrorMessage is not null
|
||||
? $"Merged with warning: {result.ErrorMessage}"
|
||||
: "Merged.";
|
||||
// Auto-close after a short delay.
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1200);
|
||||
Avalonia.Threading.Dispatcher.UIThread.Post(() => CloseAction?.Invoke());
|
||||
});
|
||||
break;
|
||||
case "conflict":
|
||||
HasConflict = true;
|
||||
ConflictFiles = result.ConflictFiles;
|
||||
ErrorMessage = "Merge conflict — target branch restored. Resolve manually or via Continue, then retry.";
|
||||
break;
|
||||
case "blocked":
|
||||
ErrorMessage = $"Blocked: {result.ErrorMessage}";
|
||||
break;
|
||||
default:
|
||||
ErrorMessage = $"Unknown status: {result.Status}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"Merge failed: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Cancel() => CloseAction?.Invoke();
|
||||
}
|
||||
Reference in New Issue
Block a user