mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
fix(ui): skeletons that match the M3U header and dashboard rails (#1887)
* fix(ui): skeletons that match the M3U header and dashboard rails Loading skeletons now take their boxes from the code that draws the content, so the content lands where the skeleton was. - M3U channel list: the loading state's headers, divider, groups rail and first row share `styles/_channel-list-layout.scss` with the All channels, context and groups views. The All channels list moved down by 19px when a playlist loaded; the group header was 79px against the real 69px (the panel header's 52px minimum plus the padding that `.sidebar .sidebar-header` gives it, now one mixin both include). Group rows start at the list's top-left like the virtual-scroll rows. - Dashboard rails: `lib-dashboard-rail-skeleton` replaces six copies of the skeleton markup and takes the rail's layout and aspect ratio (channel cards for live favorites, 16:9 for sources, posters with a title and a meta line otherwise). Its header, track, cards and text line boxes come from `_dashboard-rail-geometry.scss`, which the rail itself now uses. The skeleton was 328px against the 337.8px poster rail and a 2:3 poster in front of the 83.5px channel cards. - One shimmer in `libs/ui/styles/_skeleton-shimmer.scss`, mixed from `--app-on-surface`: the rail sweep between the widget surfaces was 1.04:1 on the light theme. The dashboard hero and the detail skeletons (through a forward) use it too. - Electron E2E `skeleton-geometry.e2e.ts` holds the IPC channel behind each loading state, compares skeleton and content boxes to 0.5px at 1280x700 and caps the dashboard layout-shift score at 0.01. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(dashboard): keep rail heights when a skeleton stands for a label-less or live rail Review follow-up (Greptile, local): - Rails that replace a skeleton (Continue Watching, sources, Xtream recently added, TMDB) keep an empty meta line under cards without labels (`reserveMetaLine`), as the skeleton always draws one; a Continue Watching rail of movies with no known duration came in shorter. - The recent-content skeleton follows the recent rail it waits for: posters while Continue Watching is still loading with no cards, channel cards otherwise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ea7efdbd9b
commit
7d7d691ce4
34 files changed
+1747
-465
No files matched your search
@@ -745,6 +745,30 @@ mirrors the row/card geometry it precedes (see Channel List Item and Cover
|
||||
Grids). The unified Favorites/Recent page gates this on `isLoading`, set only
|
||||
while its item list is empty.
|
||||
|
||||
A skeleton takes its boxes from the code that draws the content, not from
|
||||
copied values: the same Sass mixins for every box that sets a height (header
|
||||
padding and minimum height, row and card size, track bleed, gaps), and for
|
||||
text a line box of the real font size and line height with a shorter shimmer
|
||||
bar inside it. Copied values drift: the M3U channel list moved down by 19 px
|
||||
when its playlist loaded, and the dashboard rail skeletons were 2:3 posters
|
||||
in front of 84 px channel cards.
|
||||
|
||||
- The channel list views and `app-channel-list-loading-state` share
|
||||
`libs/ui/components/src/lib/styles/_channel-list-layout.scss`. The loading
|
||||
state takes the view and hides its headers where the loaded view does
|
||||
(`showHeader`, compact lists).
|
||||
- The dashboard rails and `lib-dashboard-rail-skeleton` share
|
||||
`libs/workspace/dashboard/feature/src/lib/rails/_dashboard-rail-geometry.scss`.
|
||||
A skeleton takes the `layout` and `aspectRatio` of the rail it precedes:
|
||||
channel cards for live favorites, 16:9 cards for sources, posters with a
|
||||
title and a meta line otherwise.
|
||||
- `apps/electron-backend-e2e/src/skeleton-geometry.e2e.ts` holds the IPC
|
||||
channel that answers each loading state, compares the skeleton's header,
|
||||
first-row and rail boxes with the content's to 0.5 px at 1280×700, and keeps
|
||||
the dashboard's layout-shift score from skeleton to content at or under
|
||||
0.01. A whole subtree swapped for new nodes does not count as a layout
|
||||
shift, so the positions are compared directly.
|
||||
|
||||
### Pages of independently loading blocks: delayed skeletons
|
||||
|
||||
The dashboard renders each rail as soon as its own data arrives, and several
|
||||
@@ -770,9 +794,13 @@ page down. For the same reason it stays until every source that can fill it
|
||||
has loaded, not only the first one. Use the same rules for any page that
|
||||
stacks independently loading blocks.
|
||||
|
||||
Skeleton blocks use one shimmer, `libs/ui/styles/_skeleton-shimmer.scss`: a
|
||||
fill mixed from `--app-on-surface` (8%, with a 16% highlight), so it reads on
|
||||
both themes. The dashboard rails used to sweep between `--app-widget-bg` and
|
||||
`--app-widget-header-bg`, 1.04:1 apart on the light theme.
|
||||
|
||||
The movie and series detail hero has an immediate skeleton too, built from
|
||||
the shared shimmer (`libs/ui/components/src/lib/styles/_skeleton-shimmer.scss`)
|
||||
at the loaded hero's geometry: stage height, bottom-aligned eyebrow, title,
|
||||
the shared shimmer at the loaded hero's geometry: stage height, bottom-aligned eyebrow, title,
|
||||
one row of chips, two description lines, the action row and two credit lines.
|
||||
The Xtream series page adds an episodes section skeleton under it, and the
|
||||
Stalker series hero holds the Play button's place
|
||||
|
||||
@@ -605,6 +605,9 @@ channel-list-container/
|
||||
`setFavorites` action: doing so reads and rewrites the complete M3U payload
|
||||
again just to store favorites that already came from SQLite.
|
||||
- `ChannelListContainerComponent` now renders a dedicated skeleton state while `channelsLoading` is true.
|
||||
Its headers and first row share the views' boxes
|
||||
(`libs/ui/components/src/lib/styles/_channel-list-layout.scss`), so the list
|
||||
does not move when the channels land.
|
||||
- `ChannelListContainerComponent` no longer clears `channels` on destroy; route/session code is the single owner of shared list lifecycle during navigation.
|
||||
- The dedicated `/workspace/playlists/:id/favorites` and `/workspace/playlists/:id/recent` collection routes do not drive the shared sidebar channel list; they default to the `playlist` scope so rail links always open the current playlist view, not the last persisted global scope.
|
||||
- M3U favorites and recent collection rows preserve their full `Channel` payload on unified live items so the shared live list can open the read-only channel details context menu without reconstructing partial channel data.
|
||||
|
||||
@@ -70,7 +70,10 @@ Render rules:
|
||||
1. Dashboard rails render independently as their data sources resolve. The
|
||||
page no longer uses `dashboardReady()` as a page-wide skeleton gate.
|
||||
Initial hero/recent/favorites loading states render scoped skeletons so one
|
||||
slow rail does not hide already available content.
|
||||
slow rail does not hide already available content. A rail skeleton is a
|
||||
`lib-dashboard-rail-skeleton` with the layout of the rail it precedes,
|
||||
sized from the rail's own geometry partial, so the rail replaces it at the
|
||||
same height (UI guidelines, Loading States).
|
||||
2. `hasPlaylists() === false` → render `<app-empty-state [type]="'welcome-dashboard'">`
|
||||
full-bleed. All rails and the hero are skipped.
|
||||
3. The hero (`lib-dashboard-hero`) renders when it has at least one slide;
|
||||
|
||||
Reference in new issue
Block a user