feat(ui): My Day actions, orphan-aware grouping, menu restructure
Pending UI work: - My Day add/remove context actions on task rows (parent removal cascades to children) - orphan-aware grouping: a child whose parent isn't in view renders as a top-level row, not an indented draft - shell menu restructure (Worker / Repositories submenus); 'Finalize plan' action, drop 'Queue subtasks sequentially' - notes editor refinements - subtask-row hover tweak (Surface3, no transition) - bump Avalonia 12.0.0 -> 12.0.4
This commit is contained in:
@@ -334,6 +334,10 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
// Items is already ordered by SortOrder from the DB query.
|
||||
// Treat rows whose ParentTaskId is not in the current view as orphans -> top-level.
|
||||
var visibleIds = Items.Select(r => r.Id).ToHashSet();
|
||||
// A child reads as a child only while its parent is in the view. Flag orphans so they
|
||||
// render flat (no indent, no Draft/Planned badge) instead of breaking the layout.
|
||||
foreach (var r in Items)
|
||||
r.ParentInView = string.IsNullOrEmpty(r.ParentTaskId) || visibleIds.Contains(r.ParentTaskId!);
|
||||
bool IsTopLevel(TaskRowViewModel r) =>
|
||||
!r.IsChild
|
||||
|| string.IsNullOrEmpty(r.ParentTaskId)
|
||||
@@ -571,6 +575,52 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
TasksChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddToMyDayAsync(TaskRowViewModel? row)
|
||||
{
|
||||
if (row is null || row.IsMyDay) return;
|
||||
row.IsMyDay = true;
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity != null)
|
||||
{
|
||||
entity.IsMyDay = true;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
TasksChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RemoveFromMyDayAsync(TaskRowViewModel? row)
|
||||
{
|
||||
if (row is null) return;
|
||||
row.IsMyDay = false;
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
// Removing a parent takes its whole plan off My Day: clear the task and every child, so no
|
||||
// orphaned child is left behind (independently-IsMyDay children included). A leaf child has
|
||||
// no children of its own, so this collapses to just clearing the row itself.
|
||||
var affected = await db.Tasks
|
||||
.Where(t => t.Id == row.Id || t.ParentTaskId == row.Id)
|
||||
.ToListAsync();
|
||||
foreach (var t in affected)
|
||||
t.IsMyDay = false;
|
||||
if (affected.Count > 0)
|
||||
await db.SaveChangesAsync();
|
||||
if (_currentList?.Id == "smart:my-day")
|
||||
{
|
||||
var drop = Items
|
||||
.Where(r => r.Id == row.Id || r.ParentTaskId == row.Id)
|
||||
.ToList();
|
||||
foreach (var r in drop)
|
||||
Items.Remove(r);
|
||||
}
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
TasksChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public async Task SetStatusOnRowAsync(TaskRowViewModel row, TaskStatus status)
|
||||
{
|
||||
if (_worker is null) return;
|
||||
@@ -582,6 +632,12 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
private async Task SendToQueueAsync(TaskRowViewModel? row)
|
||||
{
|
||||
if (row is null || row.IsRunning) return;
|
||||
// A finalized planning parent queues its plan (children sequentially), not itself.
|
||||
if (row.CanQueuePlan)
|
||||
{
|
||||
await QueuePlanningSubtasksAsync(row);
|
||||
return;
|
||||
}
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity is null) return;
|
||||
|
||||
Reference in New Issue
Block a user