refactor(tags): remove tag entity and all references

Drops TagEntity, TagRepository, and tag wiring across data layer, worker,
and UI. Adds RemoveTags migration to clean up schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-05-19 08:07:24 +02:00
parent 8d34db3f9b
commit 623ebf147b
42 changed files with 333 additions and 1118 deletions

View File

@@ -31,7 +31,6 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
public ObservableCollection<TaskRowViewModel> OverdueItems { get; } = new();
public ObservableCollection<TaskRowViewModel> OpenItems { get; } = new();
public ObservableCollection<TaskRowViewModel> CompletedItems { get; } = new();
public ObservableCollection<string> AllTags { get; } = new();
[ObservableProperty] private string _newTaskTitle = "";
[ObservableProperty] private TaskRowViewModel? _selectedTask;
@@ -59,22 +58,9 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
_worker.WorktreeUpdatedEvent += OnWorkerTaskUpdated;
_worker.TaskMessageEvent += OnWorkerTaskMessage;
_worker.ConnectionRestoredEvent += () => LoadForList(_currentList);
_ = RefreshAllTagsAsync();
}
}
private async Task RefreshAllTagsAsync()
{
if (_worker is null) return;
try
{
var tags = await _worker.GetAllTagsAsync();
AllTags.Clear();
foreach (var t in tags) AllTags.Add(t);
}
catch { /* offline */ }
}
private void OnWorkerTaskMessage(string taskId, string line)
{
var row = Items.FirstOrDefault(r => r.Id == taskId);
@@ -101,7 +87,6 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
var entity = await db.Tasks
.Include(t => t.List)
.Include(t => t.Worktree)
.Include(t => t.Tags)
.FirstOrDefaultAsync(t => t.Id == taskId);
var existing = Items.FirstOrDefault(r => r.Id == taskId);
@@ -190,7 +175,6 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
var all = await db.Tasks
.Include(t => t.List)
.Include(t => t.Worktree)
.Include(t => t.Tags)
.OrderBy(t => t.SortOrder).ThenBy(t => t.CreatedAt)
.ToListAsync(ct);
@@ -484,37 +468,14 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
catch { /* offline; broadcast won't fire */ }
}
public async Task ToggleTagOnRowAsync(TaskRowViewModel row, string tagName)
{
if (_worker is null) return;
var name = tagName.Trim().ToLowerInvariant();
if (name.Length == 0) return;
var current = row.Tags.ToList();
var next = current.Contains(name)
? current.Where(t => t != name).ToList()
: current.Append(name).ToList();
try
{
await _worker.SetTaskTagsAsync(row.Id, next);
await RefreshAllTagsAsync();
}
catch { }
}
[RelayCommand]
private async Task SendToQueueAsync(TaskRowViewModel? row)
{
if (row is null || row.IsRunning) return;
await using var db = await _dbFactory.CreateDbContextAsync();
var entity = await db.Tasks.Include(t => t.Tags).FirstOrDefaultAsync(t => t.Id == row.Id);
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
if (entity is null) return;
entity.Status = TaskStatus.Queued;
// Worker queue picker requires the "agent" tag — attach it on explicit enqueue.
if (!entity.Tags.Any(t => t.Name == "agent"))
{
var agentTag = await db.Tags.FirstOrDefaultAsync(t => t.Name == "agent");
if (agentTag is not null) entity.Tags.Add(agentTag);
}
await db.SaveChangesAsync();
row.Status = TaskStatus.Queued;
if (_worker is not null)
@@ -568,6 +529,14 @@ public sealed partial class TasksIslandViewModel : ViewModelBase
TasksChanged?.Invoke(this, EventArgs.Empty);
}
[RelayCommand]
private async Task CancelRunningTaskAsync(TaskRowViewModel? row)
{
if (row is null || !row.IsRunning || _worker is null) return;
try { await _worker.CancelTaskAsync(row.Id); }
catch { /* worker offline; the broadcast will reconcile when it returns */ }
}
public async Task SetScheduledForAsync(TaskRowViewModel row, DateTime? when)
{
if (row is null) return;