Files
ClaudeDo/src/ClaudeDo.Ui/ViewModels/Modals/RepoImportModalViewModel.cs

122 lines
3.8 KiB
C#

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<ClaudeDoDbContext> _dbFactory;
private readonly HashSet<string> _existingDirs = new(StringComparer.OrdinalIgnoreCase);
public ObservableCollection<RepoImportItemViewModel> 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<ClaudeDoDbContext> 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<string> folders)
{
var current = new HashSet<string>(
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<RepoImportItemViewModel> BuildCandidates(
IEnumerable<RepoCandidate> found,
IReadOnlySet<string> currentPaths,
IReadOnlySet<string> existingDirs)
{
var items = new List<RepoImportItemViewModel>();
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();
}