using System.Collections.ObjectModel; using System.ComponentModel; using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Ui.Services; using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; namespace ClaudeDo.Ui.ViewModels.Modals; public sealed partial class RepoImportModalViewModel : ViewModelBase { private readonly IDbContextFactory _dbFactory; private readonly HashSet _existingDirs = new(StringComparer.OrdinalIgnoreCase); public ObservableCollection Repos { get; } = new(); public Action? CloseAction { get; set; } public int CreateCount => Repos.Count(r => r.IsChecked && !r.AlreadyAdded); public bool CanCreate => CreateCount > 0; public string CreateButtonText => $"Create {CreateCount} list(s)"; public RepoImportModalViewModel(IDbContextFactory dbFactory) { _dbFactory = dbFactory; } public async Task LoadAsync(CancellationToken ct = default) { Repos.Clear(); _existingDirs.Clear(); await using var ctx = await _dbFactory.CreateDbContextAsync(ct); var lists = new ListRepository(ctx); foreach (var l in await lists.GetAllAsync(ct)) { if (!string.IsNullOrWhiteSpace(l.WorkingDir)) _existingDirs.Add(l.WorkingDir!); } NotifyCreateState(); } public void AddFolders(IEnumerable folders) { var current = new HashSet( Repos.Select(r => r.FullPath), StringComparer.OrdinalIgnoreCase); foreach (var folder in folders) { var found = RepoScanner.Scan(folder); foreach (var item in BuildCandidates(found, current, _existingDirs)) { item.PropertyChanged += OnItemChanged; Repos.Add(item); current.Add(item.FullPath); } } NotifyCreateState(); } public static List BuildCandidates( IEnumerable found, IReadOnlySet currentPaths, IReadOnlySet existingDirs) { var items = new List(); foreach (var c in found) { if (currentPaths.Contains(c.FullPath)) continue; items.Add(new RepoImportItemViewModel { Name = c.Name, FullPath = c.FullPath, AlreadyAdded = existingDirs.Contains(c.FullPath), IsChecked = true, }); } return items; } private void OnItemChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(RepoImportItemViewModel.IsChecked)) NotifyCreateState(); } private void NotifyCreateState() { OnPropertyChanged(nameof(CreateCount)); OnPropertyChanged(nameof(CanCreate)); OnPropertyChanged(nameof(CreateButtonText)); } [RelayCommand] private async Task CreateAsync() { var toCreate = Repos.Where(r => r.IsChecked && !r.AlreadyAdded).ToList(); if (toCreate.Count > 0) { await using var ctx = await _dbFactory.CreateDbContextAsync(); var lists = new ListRepository(ctx); foreach (var r in toCreate) { await lists.AddAsync(new ListEntity { Id = Guid.NewGuid().ToString("N"), Name = r.Name, WorkingDir = r.FullPath, DefaultCommitType = CommitTypeRegistry.DefaultType, CreatedAt = DateTime.UtcNow, }); } } CloseAction?.Invoke(); } [RelayCommand] private void Cancel() => CloseAction?.Invoke(); }