General Changes

This commit is contained in:
mika kuns
2026-04-20 09:54:13 +02:00
parent 2a8cd97d02
commit eff1045e63
4 changed files with 56 additions and 4 deletions

View File

@@ -28,6 +28,7 @@ public partial class MainWindowViewModel : ViewModelBase, IDisposable
public StatusBarViewModel StatusBar { get; } public StatusBarViewModel StatusBar { get; }
private readonly Action<string> _onTaskChanged; private readonly Action<string> _onTaskChanged;
private readonly Action<string> _onTaskSubtasksChanged;
public MainWindowViewModel( public MainWindowViewModel(
IDbContextFactory<ClaudeDoDbContext> dbFactory, IDbContextFactory<ClaudeDoDbContext> dbFactory,
@@ -45,13 +46,20 @@ public partial class MainWindowViewModel : ViewModelBase, IDisposable
StatusBar = statusBar; StatusBar = statusBar;
_onTaskChanged = taskId => _ = TaskList.RefreshSingleAsync(taskId); _onTaskChanged = taskId => _ = TaskList.RefreshSingleAsync(taskId);
_onTaskSubtasksChanged = taskId =>
{
if (TaskDetail.CurrentTaskId == taskId)
_ = TaskDetail.RefreshSubtasksFromDbAsync();
};
TaskList.SelectedTaskChanged += OnSelectedTaskChanged; TaskList.SelectedTaskChanged += OnSelectedTaskChanged;
TaskList.TaskSubtasksChanged += _onTaskSubtasksChanged;
TaskDetail.TaskChanged += _onTaskChanged; TaskDetail.TaskChanged += _onTaskChanged;
} }
public void Dispose() public void Dispose()
{ {
TaskList.SelectedTaskChanged -= OnSelectedTaskChanged; TaskList.SelectedTaskChanged -= OnSelectedTaskChanged;
TaskList.TaskSubtasksChanged -= _onTaskSubtasksChanged;
TaskDetail.TaskChanged -= _onTaskChanged; TaskDetail.TaskChanged -= _onTaskChanged;
} }

View File

@@ -51,6 +51,7 @@ public partial class TaskDetailViewModel : ViewModelBase
public ObservableCollection<SubtaskItemViewModel> Subtasks { get; } = new(); public ObservableCollection<SubtaskItemViewModel> Subtasks { get; } = new();
private string? _taskId; private string? _taskId;
public string? CurrentTaskId => _taskId;
private string? _listId; private string? _listId;
private bool _isLoading; private bool _isLoading;
// Cancels an in-flight LoadAsync when a new TaskUpdated event arrives // Cancels an in-flight LoadAsync when a new TaskUpdated event arrives
@@ -267,11 +268,13 @@ public partial class TaskDetailViewModel : ViewModelBase
var vm = SubtaskItemViewModel.From(entity); var vm = SubtaskItemViewModel.From(entity);
vm.PropertyChanged += OnSubtaskPropertyChanged; vm.PropertyChanged += OnSubtaskPropertyChanged;
Subtasks.Add(vm); Subtasks.Add(vm);
TaskChanged?.Invoke(_taskId);
} }
[RelayCommand] [RelayCommand]
private async Task RemoveSubtask(SubtaskItemViewModel item) private async Task RemoveSubtask(SubtaskItemViewModel item)
{ {
if (_taskId is null) return;
if (!string.IsNullOrEmpty(item.Id)) if (!string.IsNullOrEmpty(item.Id))
{ {
using var context = _dbFactory.CreateDbContext(); using var context = _dbFactory.CreateDbContext();
@@ -280,6 +283,7 @@ public partial class TaskDetailViewModel : ViewModelBase
} }
item.PropertyChanged -= OnSubtaskPropertyChanged; item.PropertyChanged -= OnSubtaskPropertyChanged;
Subtasks.Remove(item); Subtasks.Remove(item);
TaskChanged?.Invoke(_taskId);
} }
private async void OnSubtaskPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) private async void OnSubtaskPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
@@ -301,6 +305,8 @@ public partial class TaskDetailViewModel : ViewModelBase
OrderNum = Subtasks.IndexOf(vm), OrderNum = Subtasks.IndexOf(vm),
CreatedAt = orig?.CreatedAt ?? DateTime.UtcNow, CreatedAt = orig?.CreatedAt ?? DateTime.UtcNow,
}); });
if (e.PropertyName == nameof(SubtaskItemViewModel.Completed))
TaskChanged?.Invoke(_taskId);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -309,6 +315,35 @@ public partial class TaskDetailViewModel : ViewModelBase
} }
} }
public async Task RefreshSubtasksFromDbAsync()
{
if (_taskId is null) return;
List<SubtaskEntity> subtasks;
using (var context = _dbFactory.CreateDbContext())
{
var subtaskRepo = new SubtaskRepository(context);
subtasks = await subtaskRepo.GetByTaskIdAsync(_taskId);
}
_isLoading = true;
try
{
foreach (var old in Subtasks) old.PropertyChanged -= OnSubtaskPropertyChanged;
Subtasks.Clear();
foreach (var s in subtasks)
{
var vm = SubtaskItemViewModel.From(s);
vm.PropertyChanged += OnSubtaskPropertyChanged;
Subtasks.Add(vm);
}
}
finally
{
_isLoading = false;
}
}
public void SetAgentFromPath(string path) public void SetAgentFromPath(string path)
{ {
var existing = AvailableAgents.FirstOrDefault(a => a.Path == path); var existing = AvailableAgents.FirstOrDefault(a => a.Path == path);

View File

@@ -32,13 +32,15 @@ public partial class TaskItemViewModel : ViewModelBase
private readonly Func<string, Task>? _runNow; private readonly Func<string, Task>? _runNow;
private readonly Func<bool> _canRunNow; private readonly Func<bool> _canRunNow;
private readonly Func<string, Task>? _toggleDone; private readonly Func<string, Task>? _toggleDone;
private readonly Action<string>? _onSubtasksChanged;
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory; private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
private bool _subtasksLoaded; private bool _subtasksLoaded;
public TaskItemViewModel(TaskEntity entity, IReadOnlyList<TagEntity> tags, public TaskItemViewModel(TaskEntity entity, IReadOnlyList<TagEntity> tags,
Func<string, Task>? runNow, Func<bool> canRunNow, Func<string, Task>? runNow, Func<bool> canRunNow,
IDbContextFactory<ClaudeDoDbContext> dbFactory, int subtaskCount, IDbContextFactory<ClaudeDoDbContext> dbFactory, int subtaskCount,
Func<string, Task>? toggleDone = null) Func<string, Task>? toggleDone = null,
Action<string>? onSubtasksChanged = null)
{ {
Entity = entity; Entity = entity;
Id = entity.Id; Id = entity.Id;
@@ -52,6 +54,7 @@ public partial class TaskItemViewModel : ViewModelBase
_runNow = runNow; _runNow = runNow;
_canRunNow = canRunNow; _canRunNow = canRunNow;
_toggleDone = toggleDone; _toggleDone = toggleDone;
_onSubtasksChanged = onSubtasksChanged;
_dbFactory = dbFactory; _dbFactory = dbFactory;
_subtaskCount = subtaskCount; _subtaskCount = subtaskCount;
_hasSubtasks = subtaskCount > 0; _hasSubtasks = subtaskCount > 0;
@@ -154,6 +157,8 @@ public partial class TaskItemViewModel : ViewModelBase
entity.Completed = vm.Completed; entity.Completed = vm.Completed;
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
_onSubtasksChanged?.Invoke(Id);
} }
public async Task RefreshSubtasksAsync(int newCount) public async Task RefreshSubtasksAsync(int newCount)

View File

@@ -29,10 +29,14 @@ public partial class TaskListViewModel : ViewModelBase
[ObservableProperty] private string _inlineAddTitle = ""; [ObservableProperty] private string _inlineAddTitle = "";
public event Action<TaskItemViewModel?>? SelectedTaskChanged; public event Action<TaskItemViewModel?>? SelectedTaskChanged;
public event Action<string>? TaskSubtasksChanged;
partial void OnSelectedTaskChanged(TaskItemViewModel? value) => partial void OnSelectedTaskChanged(TaskItemViewModel? value) =>
SelectedTaskChanged?.Invoke(value); SelectedTaskChanged?.Invoke(value);
private void NotifySubtasksChanged(string taskId) =>
TaskSubtasksChanged?.Invoke(taskId);
public TaskListViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, WorkerClient worker, public TaskListViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, WorkerClient worker,
Func<TaskEditorViewModel> editorFactory, Action<string> showMessage) Func<TaskEditorViewModel> editorFactory, Action<string> showMessage)
{ {
@@ -101,7 +105,7 @@ public partial class TaskListViewModel : ViewModelBase
var tags = await taskRepo.GetEffectiveTagsAsync(e.Id); var tags = await taskRepo.GetEffectiveTagsAsync(e.Id);
subtaskCounts.TryGetValue(e.Id, out var count); subtaskCounts.TryGetValue(e.Id, out var count);
Tasks.Add(new TaskItemViewModel(e, tags, RunNowAsync, () => _worker.IsConnected, Tasks.Add(new TaskItemViewModel(e, tags, RunNowAsync, () => _worker.IsConnected,
_dbFactory, count, ToggleDoneAsync)); _dbFactory, count, ToggleDoneAsync, NotifySubtasksChanged));
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -143,7 +147,7 @@ public partial class TaskListViewModel : ViewModelBase
await taskRepo.AddAsync(entity); await taskRepo.AddAsync(entity);
var tags = await taskRepo.GetEffectiveTagsAsync(entity.Id); var tags = await taskRepo.GetEffectiveTagsAsync(entity.Id);
var vm = new TaskItemViewModel(entity, tags, RunNowAsync, () => _worker.IsConnected, var vm = new TaskItemViewModel(entity, tags, RunNowAsync, () => _worker.IsConnected,
_dbFactory, 0, ToggleDoneAsync); _dbFactory, 0, ToggleDoneAsync, NotifySubtasksChanged);
Tasks.Add(vm); Tasks.Add(vm);
SelectedTask = vm; SelectedTask = vm;
InlineAddTitle = ""; InlineAddTitle = "";
@@ -195,7 +199,7 @@ public partial class TaskListViewModel : ViewModelBase
var tags = await taskRepo.GetEffectiveTagsAsync(saved.Id); var tags = await taskRepo.GetEffectiveTagsAsync(saved.Id);
Tasks.Add(new TaskItemViewModel(saved, tags, RunNowAsync, () => _worker.IsConnected, Tasks.Add(new TaskItemViewModel(saved, tags, RunNowAsync, () => _worker.IsConnected,
_dbFactory, 0, ToggleDoneAsync)); _dbFactory, 0, ToggleDoneAsync, NotifySubtasksChanged));
// Auto wake-queue if agent+queued // Auto wake-queue if agent+queued
if (saved.Status == TaskStatus.Queued && if (saved.Status == TaskStatus.Queued &&