Skip to content

Commit c57a4e1

Browse files
committed
fix(settings): hide non-functional Latest Media toggle for Playlists/Boxsets; TV auto-focus in Home Sections
Playlists and Boxsets use a different API path for row content (getItems with type filtering rather than getLatestItems on a parent). Hiding the 'Show in Latest Media' toggle for these types avoids surfacing a control that has no effect and previously caused duplicate rows. Books correctly retain the toggle � Jellyfin's latestItemsExcludes mechanism works for book libraries and the toggle is fully functional. In Home Sections (TV leanback), after the empty-state probe completes the first available section row is now auto-focused via a postFrameCallback so the user does not need to press D-pad before navigating. Also removes the redundant _checkEmptyStates() call from initState (already driven by _refreshPluginSections).
1 parent 47c62f4 commit c57a4e1

2 files changed

Lines changed: 39 additions & 36 deletions

File tree

lib/ui/screens/settings/home_sections_screen.dart

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ class _HomeSectionsScreenState extends State<HomeSectionsScreen> {
8686
final _focusNodes = <FocusNode>[];
8787

8888
final Set<String> _emptySectionIds = {};
89-
bool _isLoadingEmptyStates = false;
9089

9190
static FavoriteTypeFilter _favoriteFilterForSection(HomeSectionType type) {
9291
return switch (type) {
@@ -104,9 +103,6 @@ class _HomeSectionsScreenState extends State<HomeSectionsScreen> {
104103

105104
Future<void> _checkEmptyStates() async {
106105
if (!mounted) return;
107-
setState(() {
108-
_isLoadingEmptyStates = true;
109-
});
110106

111107
final client = GetIt.instance<MediaServerClient>();
112108
final userId = client.userId;
@@ -306,8 +302,14 @@ class _HomeSectionsScreenState extends State<HomeSectionsScreen> {
306302
_emptySectionIds
307303
..clear()
308304
..addAll(newEmptyIds);
309-
_isLoadingEmptyStates = false;
310305
});
306+
if (PlatformDetection.isTV) {
307+
WidgetsBinding.instance.addPostFrameCallback((_) {
308+
if (!mounted) return;
309+
final first = _visibleSectionIndices().firstOrNull;
310+
if (first != null) _focusSectionAndEnsureVisible(first);
311+
});
312+
}
311313
}
312314
}
313315

@@ -383,7 +385,8 @@ class _HomeSectionsScreenState extends State<HomeSectionsScreen> {
383385
_persistSections(pushSync: false);
384386
}
385387
_refreshPluginSections();
386-
unawaited(_checkEmptyStates());
388+
// Empty-state check is driven entirely by _refreshPluginSections;
389+
// no separate call here avoids a double-spinner flash on open.
387390
}
388391

389392
bool _ensureBuiltinSectionsPresent() {
@@ -418,6 +421,8 @@ class _HomeSectionsScreenState extends State<HomeSectionsScreen> {
418421
return changed;
419422
}
420423

424+
static const _noLatestMediaSupport = {'playlists', 'boxsets'};
425+
421426
/// Probes for newly discovered plugin sections in the background and
422427
/// re-merges the result into the visible list.
423428
Future<void> _refreshPluginSections() async {
@@ -1064,19 +1069,6 @@ class _HomeSectionsScreenState extends State<HomeSectionsScreen> {
10641069
context,
10651070
Text(l10n.homeSections),
10661071
actions: [
1067-
if (_isLoadingEmptyStates)
1068-
const Padding(
1069-
padding: EdgeInsets.symmetric(horizontal: 12),
1070-
child: Center(
1071-
child: SizedBox(
1072-
width: 18,
1073-
height: 18,
1074-
child: CircularProgressIndicator(
1075-
strokeWidth: 2,
1076-
),
1077-
),
1078-
),
1079-
),
10801072
IconButton(
10811073
icon: const Icon(Icons.restore),
10821074
tooltip: l10n.resetToDefaults,

lib/ui/screens/settings/library_settings_screen.dart

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,27 +147,38 @@ class _LibraryVisibilityScreenState extends State<LibraryVisibilityScreen> {
147147
onChanged: (v) => _toggleExclude(lib.id, !v, isLatest: false),
148148
),
149149
),
150-
TvFocusHighlight(
151-
builder: (_, focused) => SwitchListTile(
152-
secondary: Icon(Icons.new_releases,
153-
color: focused
154-
? AppColors.black.withValues(alpha: 0.54)
155-
: null),
156-
title: Text(
157-
l10n.showInLatestMedia,
158-
style: TextStyle(
159-
fontSize: 14,
160-
fontWeight: FontWeight.w600,
161-
color: focused
162-
? AppColors.black.withValues(alpha: 0.87)
163-
: AppColorScheme.onSurface,
150+
// Playlists and boxsets use a different API path for their row content
151+
// (getItems with type filtering rather than getLatestItems on a parent).
152+
// Until a dedicated Latest row type exists for these, hide the toggle
153+
// to avoid surfacing a setting that has no effect.
154+
if (!_noLatestMediaSupport.contains(lib.collectionType.toLowerCase()))
155+
TvFocusHighlight(
156+
builder: (_, focused) => SwitchListTile(
157+
secondary: Icon(Icons.new_releases,
158+
color: focused
159+
? AppColors.black.withValues(alpha: 0.54)
160+
: null),
161+
title: Text(
162+
l10n.showInLatestMedia,
163+
style: TextStyle(
164+
fontSize: 14,
165+
fontWeight: FontWeight.w600,
166+
color: focused
167+
? AppColors.black.withValues(alpha: 0.87)
168+
: AppColorScheme.onSurface,
169+
),
164170
),
171+
value: !config.latestItemsExcludes.contains(lib.id),
172+
onChanged: (v) => _toggleExclude(lib.id, !v, isLatest: true),
165173
),
166-
value: !config.latestItemsExcludes.contains(lib.id),
167-
onChanged: (v) => _toggleExclude(lib.id, !v, isLatest: true),
168174
),
169-
),
170175
],
171176
];
172177
}
178+
179+
/// Collection types whose 'Show in Latest Media' toggle is not yet functional.
180+
/// getLatestItems on these parents returns container contents (movies, episodes)
181+
/// rather than the container items themselves, which causes duplicate rows.
182+
static const _noLatestMediaSupport = {'playlists', 'boxsets'};
183+
173184
}

0 commit comments

Comments
 (0)