feat(ui): wählbares Listen-Icon und aufgeräumter Task-Detail-Header
Changelog / changelog (push) Successful in 2s
Release / release (push) Successful in 41s

Listen bekommen eine Icon-Spalte (Migration AddListIcon) und einen Icon-Picker
in den Listeneinstellungen; 34 kuratierte Geometrien aus der Icon-Bibliothek,
Fallback auf "Folder" bei unbekanntem Key.

Der Metadata-Footer im Task-Detail entfällt; created-at wandert in die
Header-Zeile neben das ID-Badge, der Close-Button (und CloseDetailsCommand)
fällt weg.
This commit is contained in:
mika kuns
2026-08-28 14:17:38 +02:00
parent 22e70ffcb4
commit c31f79d3d4
18 changed files with 1189 additions and 64 deletions
@@ -295,7 +295,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
private bool _suppressTitleSave;
private CancellationTokenSource? _titleSaveCts;
// Set by shell so CloseDetailCommand can clear SelectedTask
// Set by shell so deleting a task can clear SelectedTask
public Action? CloseDetail { get; set; }
// Set by shell so DeleteTaskCommand can remove from list
@@ -1008,9 +1008,6 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
}
}
[RelayCommand]
private void CloseDetails() => CloseDetail?.Invoke();
[RelayCommand]
private async System.Threading.Tasks.Task ToggleStarAsync()
{
@@ -24,6 +24,6 @@ public sealed partial class ListNavItemViewModel : ViewModelBase
// Set while a dragged task hovers over this row as a move target — distinct from
// DropHintAbove/Below, which mark an insertion point for list reordering.
[ObservableProperty] private bool _isTaskDropTarget;
public string? IconKey { get; init; }
public string? DotColorKey { get; init; }
// Settable (not init) so a list-settings icon change refreshes the row in place.
[ObservableProperty] private string? _iconKey;
}
@@ -54,7 +54,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
if (row is null || Dialogs is null || _services is null) return;
var rawId = row.Id.StartsWith("user:", StringComparison.Ordinal) ? row.Id["user:".Length..] : row.Id;
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
await vm.LoadAsync(rawId, row.Name, row.WorkingDir, row.DefaultCommitType, row.IsManual, row.FindingsTracked);
await vm.LoadAsync(rawId, row.Name, row.WorkingDir, row.DefaultCommitType, row.IsManual, row.FindingsTracked, row.IconKey);
await Dialogs.ShowListSettingsAsync(vm);
if (vm.Deleted) await LoadAsync();
else await RefreshRowAsync(row.Id);
@@ -293,8 +293,6 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
var lists = new ListRepository(ctx);
var seedNames = new HashSet<string>(new[] { "My Day", "Important", "Planned" });
var dotColors = new[] { "Moss", "Peat", "Sage" };
int idx = 0;
foreach (var l in await lists.GetAllAsync(ct))
{
if (seedNames.Contains(l.Name)) continue;
@@ -304,8 +302,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
Id = $"user:{l.Id}",
Name = l.Name,
Kind = ListKind.User,
IconKey = "Folder",
DotColorKey = dotColors[idx % dotColors.Length],
IconKey = l.Icon ?? "Folder",
WorkingDir = l.WorkingDir,
DefaultCommitType = l.DefaultCommitType,
IsManual = l.IsManual,
@@ -314,7 +311,6 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
};
Items.Add(item);
UserLists.Add(item);
idx++;
}
ok = true;
}
@@ -395,7 +391,6 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
Name = entity.Name,
Kind = ListKind.User,
IconKey = "Folder",
DotColorKey = "Moss",
WorkingDir = entity.WorkingDir,
DefaultCommitType = entity.DefaultCommitType,
};
@@ -407,7 +402,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
if (Dialogs is not null && _services is not null)
{
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
await vm.LoadAsync(entity.Id, entity.Name, entity.WorkingDir, entity.DefaultCommitType, entity.IsManual, entity.FindingsTracked);
await vm.LoadAsync(entity.Id, entity.Name, entity.WorkingDir, entity.DefaultCommitType, entity.IsManual, entity.FindingsTracked, entity.Icon);
await Dialogs.ShowListSettingsAsync(vm);
if (vm.Deleted) await LoadAsync();
else await RefreshRowAsync(item.Id);
@@ -507,6 +502,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
row.DefaultCommitType = entity.DefaultCommitType;
row.IsManual = entity.IsManual;
row.FindingsTracked = entity.FindingsTracked;
row.IconKey = entity.Icon ?? "Folder";
row.HasTicketProject = hasTicketProject;
RecomputeHasNoLinkedRepo();
}
@@ -547,14 +543,12 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
/// </summary>
private async Task AddRowAsync(ListRepository lists, ListEntity entity, bool hasTicketProject = false)
{
var dotColors = new[] { "Moss", "Peat", "Sage" };
var item = new ListNavItemViewModel
{
Id = $"user:{entity.Id}",
Name = entity.Name,
Kind = ListKind.User,
IconKey = "Folder",
DotColorKey = dotColors[UserLists.Count % dotColors.Length],
IconKey = entity.Icon ?? "Folder",
WorkingDir = entity.WorkingDir,
DefaultCommitType = entity.DefaultCommitType,
IsManual = entity.IsManual,