refactor: extract interfaces to Interfaces folders and consolidate filters
Move interface declarations into per-area Interfaces/ subfolders, merge the small task-list filter classes into StatusFilter/SmartFlagFilter, and simplify related services, converters and hub DTO handling. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +0,0 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
public sealed class ImportantFilter : ITaskListFilter
|
||||
{
|
||||
public string Id => "smart:important";
|
||||
public bool Matches(TaskEntity t) => t.IsStarred;
|
||||
public bool ShouldCount(TaskEntity t) => t.IsStarred && t.Status != TaskStatus.Done;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) => false;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
public sealed class MyDayFilter : ITaskListFilter
|
||||
{
|
||||
public string Id => "smart:my-day";
|
||||
public bool Matches(TaskEntity t) => t.IsMyDay;
|
||||
public bool ShouldCount(TaskEntity t) => t.IsMyDay && t.Status != TaskStatus.Done;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) => false;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
public sealed class PlannedFilter : ITaskListFilter
|
||||
{
|
||||
public string Id => "smart:planned";
|
||||
public bool Matches(TaskEntity t) => t.ScheduledFor != null;
|
||||
public bool ShouldCount(TaskEntity t) => t.ScheduledFor != null && t.Status != TaskStatus.Done;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) => false;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
public sealed class QueuedFilter : ITaskListFilter
|
||||
{
|
||||
public string Id => "virtual:queued";
|
||||
public bool Matches(TaskEntity t) => t.Status == TaskStatus.Queued;
|
||||
public bool ShouldCount(TaskEntity t) => t.Status == TaskStatus.Queued;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) =>
|
||||
PlanningRules.IsPlanningParent(t) &&
|
||||
PlanningRules.HasMatchingChild(t, all, c => c.Status == TaskStatus.Queued);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
public sealed class RunningFilter : ITaskListFilter
|
||||
{
|
||||
public string Id => "virtual:running";
|
||||
public bool Matches(TaskEntity t) => t.Status == TaskStatus.Running;
|
||||
public bool ShouldCount(TaskEntity t) => t.Status == TaskStatus.Running;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) =>
|
||||
PlanningRules.IsPlanningParent(t) &&
|
||||
PlanningRules.HasMatchingChild(t, all, c => c.Status == TaskStatus.Running);
|
||||
}
|
||||
16
src/ClaudeDo.Data/Filtering/Filters/SmartFlagFilter.cs
Normal file
16
src/ClaudeDo.Data/Filtering/Filters/SmartFlagFilter.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Filter for a smart list keyed off a boolean/nullable task flag
|
||||
/// (My Day, Important, Planned). Counts only non-done matches.
|
||||
/// </summary>
|
||||
public sealed class SmartFlagFilter(string id, Func<TaskEntity, bool> flag) : ITaskListFilter
|
||||
{
|
||||
public string Id => id;
|
||||
public bool Matches(TaskEntity t) => flag(t);
|
||||
public bool ShouldCount(TaskEntity t) => flag(t) && t.Status != TaskStatus.Done;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) => false;
|
||||
}
|
||||
18
src/ClaudeDo.Data/Filtering/Filters/StatusFilter.cs
Normal file
18
src/ClaudeDo.Data/Filtering/Filters/StatusFilter.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering.Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Virtual list filter matching tasks by a single status (Queued, Running).
|
||||
/// Planning parents appear contextually when they host a matching child.
|
||||
/// </summary>
|
||||
public sealed class StatusFilter(string id, TaskStatus status) : ITaskListFilter
|
||||
{
|
||||
public string Id => id;
|
||||
public bool Matches(TaskEntity t) => t.Status == status;
|
||||
public bool ShouldCount(TaskEntity t) => t.Status == status;
|
||||
public bool MatchesAsContext(TaskEntity t, IReadOnlyList<TaskEntity> all) =>
|
||||
PlanningRules.IsPlanningParent(t) &&
|
||||
PlanningRules.HasMatchingChild(t, all, c => c.Status == status);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using ClaudeDo.Data.Filtering.Filters;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Data.Filtering;
|
||||
|
||||
@@ -14,11 +15,11 @@ public sealed class TaskListFilterRegistry
|
||||
private static readonly IReadOnlyDictionary<string, ITaskListFilter> BuiltIn =
|
||||
new Dictionary<string, ITaskListFilter>(StringComparer.Ordinal)
|
||||
{
|
||||
["smart:my-day"] = new MyDayFilter(),
|
||||
["smart:important"] = new ImportantFilter(),
|
||||
["smart:planned"] = new PlannedFilter(),
|
||||
["virtual:queued"] = new QueuedFilter(),
|
||||
["virtual:running"] = new RunningFilter(),
|
||||
["smart:my-day"] = new SmartFlagFilter("smart:my-day", t => t.IsMyDay),
|
||||
["smart:important"] = new SmartFlagFilter("smart:important", t => t.IsStarred),
|
||||
["smart:planned"] = new SmartFlagFilter("smart:planned", t => t.ScheduledFor != null),
|
||||
["virtual:queued"] = new StatusFilter("virtual:queued", TaskStatus.Queued),
|
||||
["virtual:running"] = new StatusFilter("virtual:running", TaskStatus.Running),
|
||||
["virtual:review"] = new ReviewFilter(),
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public sealed class AppSettingsRepository
|
||||
return row;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(AppSettingsEntity updated, CancellationToken ct = default)
|
||||
private async Task<AppSettingsEntity> GetOrCreateTrackedRowAsync(CancellationToken ct)
|
||||
{
|
||||
var row = await _context.AppSettings
|
||||
.FirstOrDefaultAsync(s => s.Id == AppSettingsEntity.SingletonId, ct);
|
||||
@@ -32,6 +32,12 @@ public sealed class AppSettingsRepository
|
||||
row = new AppSettingsEntity { Id = AppSettingsEntity.SingletonId };
|
||||
_context.AppSettings.Add(row);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(AppSettingsEntity updated, CancellationToken ct = default)
|
||||
{
|
||||
var row = await GetOrCreateTrackedRowAsync(ct);
|
||||
|
||||
row.DefaultClaudeInstructions = updated.DefaultClaudeInstructions ?? string.Empty;
|
||||
row.DefaultModel = string.IsNullOrWhiteSpace(updated.DefaultModel) ? "sonnet" : updated.DefaultModel;
|
||||
@@ -62,13 +68,7 @@ public sealed class AppSettingsRepository
|
||||
public async Task SetRepoImportFoldersAsync(IEnumerable<string> folders, CancellationToken ct = default)
|
||||
{
|
||||
var list = folders.ToList();
|
||||
var row = await _context.AppSettings
|
||||
.FirstOrDefaultAsync(s => s.Id == AppSettingsEntity.SingletonId, ct);
|
||||
if (row is null)
|
||||
{
|
||||
row = new AppSettingsEntity { Id = AppSettingsEntity.SingletonId };
|
||||
_context.AppSettings.Add(row);
|
||||
}
|
||||
var row = await GetOrCreateTrackedRowAsync(ct);
|
||||
|
||||
row.RepoImportFolders = list.Count == 0 ? null : JsonSerializer.Serialize(list);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
|
||||
Reference in New Issue
Block a user