Commit Graph
27 Commits
Author SHA1 Message Date
edde746 1bdeca3b5f fix(libraries): give square music grids an inter-card gutter
Square-card (music) grids had zero delegate spacing - the only
separation was each card's internal 3px padding, and square shapes are
excluded from the full-card spacing path, so playlist and album tiles
sat nearly edge to edge. Square grids now get an 8px cross/main gutter
(automotive keeps its larger spacing); poster, list, and full-card
layouts are unchanged.
2026-08-17 22:44:12 +02:00
edde746 a2b3377d91 fix(libraries): stop d-pad up snapping collection and playlist grids to top
On TV, pressing UP in the library Collections or Playlists grid snapped
the list back to the top and dropped focus on the tab chips, making long
lists impossible to navigate. Default directional focus traversal scrolls
the found card into view via Scrollable.ensureVisible, whose
outer-scrollable pass routes through the NestedScrollView coordinator and
resets the inner grid position to zero on every UP press.

Give the shared paginated card grid explicit per-card d-pad navigation,
matching the browse tab: managed per-index focus nodes, row/column moves
that request focus directly, first-row UP to the tab bar, and
first-column LEFT to the sidebar. Focus changes now scroll only through
FocusableWrapper's delta-based auto-scroll.

close #1977
2026-08-17 08:58:00 +02:00
edde746 24f63a63c5 refactor(libraries): make loadItems the single library-tab load hook
BaseLibraryTabState documented an abstract loadData() that every tab "must implement", but three of the four tabs override loadItems() entirely and carried never-invoked empty loadData stubs; only the recommended tab exercised the contract, so the class docs described an extension surface that did not exist.

loadItems() is now the one overridable hook, and the shared load transaction (generation tracking, localized error mapping, post-frame onDataLoaded) lives once in the protected runLoadTransaction helper that the recommended tab and the focus tests route through. No tab's load behavior changes.
2026-08-16 16:48:02 +02:00
edde746 364cdea59b refactor(libraries): show server labels with one policy in the picker and the dropdown
The library quick picker and the libraries dropdown each hand-rolled their own BackendBadge + server-name label rendering with divergent show-when policies: the picker only labeled libraries whose titles collided, so a uniquely-titled library on a multi-server list showed its server in the dropdown but not in the picker.

Both surfaces now share library_server_label.dart — one label widget, one grouping loop over groupLibrariesByFirstAppearance, and one policy: group headers whenever the visible list spans more than one server, per-row labels only in ungrouped lists. The picker's duplicate-title heuristic is gone; its test now pins the unified policy.
2026-08-16 16:48:02 +02:00
edde746 24a041977b fix(sheets): size sheets to their content instead of 75% of the window
Sheets rendered at the host's maximum height regardless of content, so a
one-item player queue or a two-track picker filled ~75% of a desktop window
with empty space.

BottomSheetPageScaffold now always lays out Column(mainAxisSize: .min) plus
Flexible(child:), and each sheet body shrink-wraps its own scrollable. The
scaffold's shrinkWrap flag is gone: its old true branch put the child on an
unbounded axis, where an over-tall list overflowed instead of clamping and
scrolling. Measured on a 1600x1000 window, the chapter sheet goes from 750px
to 118px for one chapter and the two-column track sheet from 750px to 154px
for one audio and one subtitle track, both still clamping at the cap.

Add SheetSplitColumns for the three side-by-side sheet layouts. A bare
VerticalDivider has no intrinsic height, so it inflated those rows to the cap
on its own; the rule now paints from a Positioned.fill that cannot size the
Stack. IntrinsicHeight is not an option because a Viewport has no intrinsics.

Because sheets are bottom-anchored, a content-driven height moves the sheet's
top edge and everything above the change point. Three surfaces opt out for
that reason and say so at the call site: SubtitleSearchSheet and its language
picker keep filling, since both refilter under an autofocused field;
FiltersBottomSheet holds the outgoing page's height through its loading
transient; and RatingBottomSheet no longer hides MAL/AniList rows
asynchronously, which used to slide live rating controls down two rows several
hundred ms after open. Wrap the shared StateMessageWidget at the filters sheet
boundary rather than editing a widget with 33 filling call sites.

The host gains an AnimatedSize keyed per sheet session so nested pushes ease
while a replacing show adopts its own height, a 720px absolute height ceiling
on desktop windows only, and a min(max(25%, 96px), 60%) drag-dismiss threshold
so short sheets neither close on a nudge nor become undismissable.

Add videoControls.noAudioDevicesAvailable so the audio output page shows a
placeholder instead of a bare header while devices load.
2026-08-08 00:37:17 +02:00
edde746 a56b9a3dfb Merge the deduplication and dead-code removal pass
Consolidates duplicated logic behind shared implementations — paginated
grid tabs, focus chrome, cached remote stores, sheet selection columns,
the server artifact store and a test fixture layer — and removes code
that had become unreachable. Net reduction of about 5,500 lines with no
behaviour change.

Where a fix had landed separately in code that moved into a shared
helper, the fix was re-applied inside the helper rather than left behind
in the copy that went away.
2026-07-26 19:41:23 +02:00
edde746 316a69a1de refactor: share the paginated grid tab, cached remote store, and tile focus
- PaginatedCardGridTabState: the collections and playlists tabs were 95%
  identical; they now supply only pageSize/fetchPage/idOf instead of each
  duplicating the grid, memo, inflation budget and focus wiring.
- EtagCachedRemoteStore: the anime-lists and fribb mapping stores now share
  one download/cache/isolate-parse/conditional-GET lifecycle.
- FocusableTileStateMixin manages its own initState/didUpdateWidget/dispose
  instead of requiring every caller to forward three lifecycle hooks.

Also drops unused ServerCapabilities entries and dead code in
focusable_list_tile and music/track_row.
2026-07-26 06:09:48 +02:00
edde746 61344f7862 test: extract shared fixtures and scaffolds
Collapse duplicated setup across the suite into six shared helpers under
test/test_helpers/ and rewrite the 28 suites that were open-coding it:

  http_fixtures.dart         jsonResponse() for http.Response JSON stubs
  library_tab_scaffold.dart  pumps library tabs under their required ancestors
  multi_server_fixtures.dart MultiServerProvider wiring for widget tests
  playback_report_fakes.dart PlaybackReportCall + fake report sinks
  profile_stack.dart         production-shaped profile dependency graph
  theme.dart                 testMonoTokens for fast-settling widget tests

Net -1245 lines with no change in coverage or assertions.
2026-07-26 06:09:47 +02:00
edde746 96fd47a160 fix(libraries): filter content-type-less Jellyfin roots 2026-07-25 17:38:09 +02:00
edde746 0959cd3040 fix(libraries): exclude episodes from mixed roots
close #1675
2026-07-25 09:13:00 +02:00
edde746 b41fb4fe75 fix(ui): harden settings focus and semantics 2026-07-24 03:46:50 +02:00
edde746 f8bfecf57d fix(media): serialize browsing and metadata mutations 2026-07-24 03:46:50 +02:00
edde746 e0bf66eea8 fix(runtime): harden application service boundaries 2026-07-24 03:46:46 +02:00
edde746 b7cd4e5e9c fix(music): preserve Jellyfin collection posters 2026-07-16 14:36:48 +02:00
edde746 517c3f57ce fix(music): keep collection and playlist artwork square 2026-07-16 12:37:02 +02:00
edde746 d5f3e581df fix: eliminate cross-app consistency drift 2026-07-13 23:13:53 +02:00
edde746 ec249d2ed4 fix(jellyfin): repair music library browsing
Fixes #1557
2026-07-13 22:58:12 +02:00
edde746 6182c34448 perf(playlists): bound card inflation 2026-07-12 08:42:25 +02:00
edde746 64db84c3e2 feat(library): default clip libraries to folder grouping
close #966
2026-07-12 03:38:23 +02:00
edde746 05a631415e feat(music): music browsing UI — square cards, library groupings, artist/album screens
Un-gates music libraries; adds CardShape.square plumbing (circular
artists), artists/albums/tracks browse groupings with per-grouping
sorts, audio playlists tab, artist + album detail screens with dpad
track rows, music context-menu actions, and a stub MusicPlaybackService
seam for the upcoming engine.
2026-07-05 17:20:28 +02:00
edde746 2d30686857 fix: decouple library grouping from provider context 2026-06-30 06:57:21 +02:00
edde746 e1c0df3bd7 fix(ci): resolve sanity check failures 2026-06-14 06:55:58 +02:00
edde746 656cf1158b fix(libraries): restore tvOS empty tab focus
close #1330
2026-06-14 00:05:44 +02:00
edde746 e45bfadc14 feat(libraries): add nav quick picker
close #1144
2026-05-27 06:52:03 +02:00
edde746 9365fcf246 fix(tv): keep alpha bar alphabet visible 2026-05-25 16:17:27 +02:00
edde746 3e0f7b4f86 fix(libraries): stabilize alpha and sort controls 2026-05-17 22:36:15 +02:00
edde746 f07feaaa52 fix(plex): repair alpha jump bar 2026-05-08 05:36:39 +02:00