feat(ui): Listen und Ordner in einer gemischten Sidebar-Reihenfolge
Die oberste Sidebar-Ebene ist jetzt eine gemischte TopLevel-Collection, so dass eine Liste auch oberhalb eines Ordners stehen kann. DropAsync deckt alle vier Quelle/Ziel-Kombinationen ab, ReorderSidebarAsync numeriert die gesamte sichtbare Reihenfolge ueber lists und list_folders hinweg.
This commit is contained in:
@@ -17,10 +17,20 @@ public sealed class ListRepository
|
||||
public async Task AddAsync(ListEntity entity, CancellationToken ct = default)
|
||||
{
|
||||
NormalizeWorkingDir(entity);
|
||||
if (entity.SortOrder == 0) entity.SortOrder = await NextSortOrderAsync(ct);
|
||||
_context.Lists.Add(entity);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
/// <summary>One past the highest <c>sort_order</c> in use — across <b>both</b> tables, because
|
||||
/// the sidebar interleaves folders and top-level lists in a single order sequence.</summary>
|
||||
private async Task<int> NextSortOrderAsync(CancellationToken ct = default)
|
||||
{
|
||||
var maxList = await _context.Lists.MaxAsync(l => (int?)l.SortOrder, ct) ?? -1;
|
||||
var maxFolder = await _context.ListFolders.MaxAsync(f => (int?)f.SortOrder, ct) ?? -1;
|
||||
return Math.Max(maxList, maxFolder) + 1;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(ListEntity entity, CancellationToken ct = default)
|
||||
{
|
||||
NormalizeWorkingDir(entity);
|
||||
@@ -56,14 +66,27 @@ public sealed class ListRepository
|
||||
return await _context.Lists.OrderBy(l => l.SortOrder).ThenBy(l => l.CreatedAt).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public async Task ReorderAsync(IReadOnlyList<string> orderedListIds, CancellationToken ct = default)
|
||||
/// <summary>
|
||||
/// Writes the sidebar's visual order as one sequence spanning both tables: folders and lists
|
||||
/// share the <c>sort_order</c> number space, which is what lets a top-level list sit between
|
||||
/// two folders. A folder's own children follow it in the sequence, so ordering folders by
|
||||
/// <c>sort_order</c> and lists by <c>sort_order</c> independently still reproduces the sidebar.
|
||||
/// </summary>
|
||||
public async Task ReorderSidebarAsync(IReadOnlyList<(string Id, bool IsFolder)> ordered, CancellationToken ct = default)
|
||||
{
|
||||
var idSet = orderedListIds.ToHashSet();
|
||||
var entities = await _context.Lists.Where(l => idSet.Contains(l.Id)).ToListAsync(ct);
|
||||
for (int i = 0; i < orderedListIds.Count; i++)
|
||||
var listIds = ordered.Where(o => !o.IsFolder).Select(o => o.Id).ToHashSet();
|
||||
var folderIds = ordered.Where(o => o.IsFolder).Select(o => o.Id).ToHashSet();
|
||||
var lists = await _context.Lists.Where(l => listIds.Contains(l.Id)).ToListAsync(ct);
|
||||
var folders = await _context.ListFolders.Where(f => folderIds.Contains(f.Id)).ToListAsync(ct);
|
||||
|
||||
for (int i = 0; i < ordered.Count; i++)
|
||||
{
|
||||
var e = entities.FirstOrDefault(x => x.Id == orderedListIds[i]);
|
||||
if (e is not null) e.SortOrder = i;
|
||||
var (id, isFolder) = ordered[i];
|
||||
if (isFolder)
|
||||
{
|
||||
if (folders.FirstOrDefault(f => f.Id == id) is { } folder) folder.SortOrder = i;
|
||||
}
|
||||
else if (lists.FirstOrDefault(l => l.Id == id) is { } list) list.SortOrder = i;
|
||||
}
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
@@ -81,7 +104,7 @@ public sealed class ListRepository
|
||||
|
||||
public async Task AddFolderAsync(ListFolderEntity folder, CancellationToken ct = default)
|
||||
{
|
||||
folder.SortOrder = await _context.ListFolders.CountAsync(ct);
|
||||
folder.SortOrder = await NextSortOrderAsync(ct);
|
||||
_context.ListFolders.Add(folder);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user