feat(ui): TasksIslandViewModel with smart/virtual/user filtering
Uses IDbContextFactory directly (singleton-safe). Adds ToggleDone, ToggleStar, Select commands. Fixes pre-existing SessionTerminalView compiled-binding error on Classes property via x:CompileBindings="False". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,127 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ClaudeDo.Data;
|
||||
using ClaudeDo.Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Ui.ViewModels.Islands;
|
||||
|
||||
public sealed partial class TasksIslandViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
||||
private ListNavItemViewModel? _currentList;
|
||||
|
||||
public event EventHandler? SelectionChanged;
|
||||
|
||||
public ObservableCollection<TaskRowViewModel> Items { get; } = new();
|
||||
|
||||
[ObservableProperty] private string _newTaskTitle = "";
|
||||
[ObservableProperty] private TaskRowViewModel? _selectedTask;
|
||||
public void LoadForList(ListNavItemViewModel? list) { /* Phase 5 */ }
|
||||
partial void OnSelectedTaskChanged(TaskRowViewModel? value) =>
|
||||
[ObservableProperty] private string _headerTitle = "";
|
||||
[ObservableProperty] private string _headerEyebrow = "";
|
||||
[ObservableProperty] private string _subtitle = "";
|
||||
|
||||
public TasksIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory)
|
||||
{
|
||||
_dbFactory = dbFactory;
|
||||
}
|
||||
|
||||
public async void LoadForList(ListNavItemViewModel? list)
|
||||
{
|
||||
_currentList = list;
|
||||
Items.Clear();
|
||||
if (list is null) return;
|
||||
|
||||
HeaderTitle = list.Name;
|
||||
HeaderEyebrow = DateTime.Now.ToString("dddd · MMM dd").ToUpperInvariant();
|
||||
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var all = await db.Tasks
|
||||
.Include(t => t.List)
|
||||
.Include(t => t.Worktree)
|
||||
.ToListAsync();
|
||||
|
||||
IEnumerable<TaskEntity> filtered = list.Kind switch
|
||||
{
|
||||
ListKind.Smart when list.Id == "smart:my-day" => all.Where(t => t.IsMyDay),
|
||||
ListKind.Smart when list.Id == "smart:important" => all.Where(t => t.IsStarred),
|
||||
ListKind.Smart when list.Id == "smart:planned" => all.Where(t => t.ScheduledFor != null),
|
||||
ListKind.Virtual when list.Id == "virtual:running" => all.Where(t => t.Status == TaskStatus.Running),
|
||||
ListKind.Virtual when list.Id == "virtual:review" => all.Where(t => t.Status == TaskStatus.Done && t.Worktree?.State == WorktreeState.Active),
|
||||
ListKind.User => all.Where(t => $"user:{t.ListId}" == list.Id),
|
||||
_ => Enumerable.Empty<TaskEntity>(),
|
||||
};
|
||||
|
||||
foreach (var t in filtered)
|
||||
Items.Add(TaskRowViewModel.FromEntity(t));
|
||||
|
||||
UpdateSubtitle();
|
||||
}
|
||||
|
||||
private void UpdateSubtitle()
|
||||
{
|
||||
var open = Items.Count(i => !i.Done);
|
||||
var running = Items.Count(i => i.Status == TaskStatus.Running);
|
||||
var review = Items.Count(i => i.Status == TaskStatus.Done && i.Branch != null);
|
||||
Subtitle = $"{open} open · {running} running · {review} in review";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NewTaskTitle) || _currentList?.Kind != ListKind.User) return;
|
||||
var listId = _currentList.Id["user:".Length..];
|
||||
var entity = new TaskEntity
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
ListId = listId,
|
||||
Title = NewTaskTitle.Trim(),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
};
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
db.Tasks.Add(entity);
|
||||
await db.SaveChangesAsync();
|
||||
Items.Insert(0, TaskRowViewModel.FromEntity(entity));
|
||||
NewTaskTitle = "";
|
||||
UpdateSubtitle();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleDoneAsync(TaskRowViewModel row)
|
||||
{
|
||||
row.Done = !row.Done;
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity != null)
|
||||
{
|
||||
entity.Status = row.Done ? TaskStatus.Done : TaskStatus.Manual;
|
||||
row.Status = entity.Status;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
UpdateSubtitle();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleStarAsync(TaskRowViewModel row)
|
||||
{
|
||||
row.IsStarred = !row.IsStarred;
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity != null)
|
||||
{
|
||||
entity.IsStarred = row.IsStarred;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Select(TaskRowViewModel row) => SelectedTask = row;
|
||||
|
||||
partial void OnSelectedTaskChanged(TaskRowViewModel? value)
|
||||
{
|
||||
foreach (var i in Items) i.IsSelected = ReferenceEquals(i, value);
|
||||
SelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user