using System.Collections.ObjectModel; using ClaudeDo.Ui.Localization; 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 Branches { get; } = new(); [ObservableProperty][NotifyCanExecuteChangedFor(nameof(SubmitCommand))] private string? _selectedBranch; [ObservableProperty] private bool _removeWorktree = true; [ObservableProperty] private string _commitMessage = ""; [ObservableProperty][NotifyCanExecuteChangedFor(nameof(SubmitCommand))] private bool _isBusy; [ObservableProperty] private string? _errorMessage; [ObservableProperty] private string? _warningMessage; [ObservableProperty] private string? _successMessage; [ObservableProperty][NotifyCanExecuteChangedFor(nameof(SubmitCommand))] private bool _hasConflict; [ObservableProperty] private IReadOnlyList _conflictFiles = Array.Empty(); public Action? CloseAction { get; set; } public MergeModalViewModel(WorkerClient worker) { _worker = worker; } public async Task InitializeAsync(string taskId, string taskTitle) { TaskId = taskId; TaskTitle = taskTitle; CommitMessage = Loc.T("vm.merge.commitMessage", taskTitle); IsBusy = true; try { var targets = await _worker.GetMergeTargetsAsync(taskId); Branches.Clear(); if (targets is null) { ErrorMessage = Loc.T("vm.merge.workerOfflineBranches"); return; } foreach (var b in targets.LocalBranches) Branches.Add(b); SelectedBranch = Branches.Contains(targets.DefaultBranch) ? targets.DefaultBranch : Branches.FirstOrDefault(); } catch (Exception ex) { ErrorMessage = Loc.T("vm.merge.loadBranchesFailed", 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}" : Loc.T("vm.merge.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 = Loc.T("vm.merge.conflict"); break; case "blocked": ErrorMessage = Loc.T("vm.merge.blocked", result.ErrorMessage ?? ""); break; default: ErrorMessage = Loc.T("vm.merge.unknownStatus", result.Status); break; } } catch (Exception ex) { ErrorMessage = Loc.T("vm.merge.mergeFailed", ex.Message); } finally { IsBusy = false; } } [RelayCommand] private void Cancel() => CloseAction?.Invoke(); }