using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Services; namespace DeepDrftPublic.Client.ViewModels; /// /// State for the Cut album-detail page (/cuts/{entryKey}). Unlike /// (which resolves a single playable track for Session/Mix), a Cut is multi-track: it loads the /// release and the full ordered track list for that release. The list is fetched through the /// existing releaseId-filtered track page sorted by TrackNumber — the explicit 1-based ordinal /// (Phase 8) that the public read both sorts on and projects onto TrackDto. Scoped; every flag is /// reset per so a reused instance never bleeds across navigations. /// public class CutDetailViewModel { private readonly IReleaseDataService _releaseData; private readonly ITrackDataService _trackData; // A Cut covers the whole album in one page. Matches the gallery's page-size convention; a single // album never approaches this ceiling (the API caps PageSize at 100 regardless). private const int AlbumPageSize = 100; public ReleaseDto? Release { get; private set; } public IReadOnlyList Tracks { get; private set; } = []; public bool IsLoading { get; private set; } = true; public bool NotFound { get; private set; } public CutDetailViewModel(IReleaseDataService releaseData, ITrackDataService trackData) { _releaseData = releaseData; _trackData = trackData; } /// Seed state directly from a bridged prerender payload — no fetch. public void Restore(ReleaseDto release, IReadOnlyList tracks) { Release = release; Tracks = tracks; NotFound = false; IsLoading = false; } public async Task Load(string entryKey) { IsLoading = true; NotFound = false; Release = null; Tracks = []; try { var releaseResult = await _releaseData.GetByEntryKey(entryKey); if (releaseResult is not { Success: true, Value: { } release }) { NotFound = true; return; } Release = release; // The album's tracks via the releaseId-filtered page — an exact join, not a title string // (which collides across same-titled releases and breaks on rename). The public page // addresses the release by EntryKey; the track→release join stays on the internal int FK // (Phase 11 §3e), so use the resolved release.Id here. Sorted by TrackNumber so rows render // in saved order. A Cut with no streamable tracks simply leaves the list empty (the page // renders the header with no rows). var trackResult = await _trackData.GetPage( pageNumber: 1, pageSize: AlbumPageSize, sortColumn: "TrackNumber", releaseId: release.Id); if (trackResult is { Success: true, Value: { Items: { } items } }) Tracks = items.ToList(); } finally { IsLoading = false; } } }