diff --git a/apps/electron-backend/src/app/database/operations/content.operations.ts b/apps/electron-backend/src/app/database/operations/content.operations.ts index 3853cec71..444d4aecc 100644 --- a/apps/electron-backend/src/app/database/operations/content.operations.ts +++ b/apps/electron-backend/src/app/database/operations/content.operations.ts @@ -124,7 +124,6 @@ export async function getGlobalRecentlyAdded( const normalizedLimit = Number.isFinite(limit) ? Math.min(Math.max(Math.trunc(limit), 1), 200) : 200; - const addedOrder = sql`CAST(${schema.content.added} AS INTEGER)`; const whereConditions = [ inArray(schema.content.type, contentTypes), @@ -136,6 +135,12 @@ export async function getGlobalRecentlyAdded( whereConditions.push(eq(schema.playlists.type, playlistType)); } + // Sort by `added` directly. Xtream stores Unix-epoch timestamps as + // 10-digit numeric strings (since 2001-09-09), so lexicographic sort + // is equivalent to numeric sort. Wrapping the column in CAST(... AS + // INTEGER) — as we used to — blocks SQLite from using + // idx_content_type_added and forces a full table scan + sort on the + // entire content table (often 100k+ rows) on every dashboard load. return db .select({ ...selectContentFields(), @@ -153,7 +158,7 @@ export async function getGlobalRecentlyAdded( eq(schema.categories.playlistId, schema.playlists.id) ) .where(and(...whereConditions)) - .orderBy(desc(addedOrder)) + .orderBy(desc(schema.content.added)) .limit(normalizedLimit); } diff --git a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html index abc1c3a7a..9555f7741 100644 --- a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html +++ b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html @@ -3,46 +3,17 @@ [class.rails-page--empty]="ready() && !hasPlaylists()" data-test-id="dashboard-page" > - @if (!ready()) { - - - - @for (_ of skeletonRails; track $index) { - - } - } @else if (!hasPlaylists()) { + @if (data.playlistsLoaded() && !hasPlaylists()) { } @else { + @if (hero(); as item) { + } @else if (data.globalRecentLoading()) { + } + @if (recentlyWatchedCards().length > 0) { + } @else if (data.globalRecentLoading()) { + } + @if (favoriteCards().length > 0) { + } @else if (data.globalFavoritesLoading()) { + } + @if (sourceCards().length > 0) { + } @else if (!data.playlistsLoaded()) { + } + @if (xtreamRecentlyAddedCards().length > 0) { + } @else if ( + xtreamPlaylistCount() > 0 && data.xtreamRecentlyAddedLoading() + ) { + } }