General Changes
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 &&
|
||||||
|
|||||||
Reference in New Issue
Block a user