diff --git a/lib/screens/hub_detail_screen.dart b/lib/screens/hub_detail_screen.dart index a6ad1f66..ddba8b94 100644 --- a/lib/screens/hub_detail_screen.dart +++ b/lib/screens/hub_detail_screen.dart @@ -61,16 +61,10 @@ class _HubDetailScreenState extends State with Refreshable { final hubKey = widget.hub.hubKey; appLogger.d('Hub key: $hubKey'); - RegExpMatch? match; - // Try different patterns - match = RegExp(r'/hubs/sections/(\d+)').firstMatch(hubKey); - if (match == null) { - match = RegExp(r'/library/sections/(\d+)').firstMatch(hubKey); - } - if (match == null) { - match = RegExp(r'sections/(\d+)').firstMatch(hubKey); - } + RegExpMatch? match = RegExp(r'/hubs/sections/(\d+)').firstMatch(hubKey); + match ??= RegExp(r'/library/sections/(\d+)').firstMatch(hubKey); + match ??= RegExp(r'sections/(\d+)').firstMatch(hubKey); if (match != null) { final sectionId = match.group(1)!; diff --git a/lib/screens/libraries_screen.dart b/lib/screens/libraries_screen.dart index bf7df420..d33be135 100644 --- a/lib/screens/libraries_screen.dart +++ b/lib/screens/libraries_screen.dart @@ -201,16 +201,6 @@ class _LibrariesScreenState extends State await storage.saveLibraryOrder(libraryKeys); } - void _reorderLibraries(int oldIndex, int newIndex) { - setState(() { - if (newIndex > oldIndex) { - newIndex -= 1; - } - final library = _allLibraries.removeAt(oldIndex); - _allLibraries.insert(newIndex, library); - }); - _saveLibraryOrder(); - } Future _loadLibraryContent(String libraryKey) async { // Compute visible libraries based on current provider state @@ -1436,45 +1426,58 @@ class _SortBottomSheetState extends State<_SortBottomSheet> { // Sort options list Expanded( - child: ListView.builder( - controller: scrollController, - padding: const EdgeInsets.symmetric(vertical: 8), - itemCount: widget.sortOptions.length, - itemBuilder: (context, index) { - final sort = widget.sortOptions[index]; - final isSelected = _tempSelectedSort?.key == sort.key; + child: RadioGroup( + groupValue: _tempSelectedSort?.key, + onChanged: (value) { + final sort = widget.sortOptions.firstWhere((s) => s.key == value); + setState(() { + _tempSelectedSort = sort; + // Use default direction for newly selected sort + _tempDescending = sort.isDefaultDescending; + }); + // Apply sort immediately with default direction + widget.onSortChanged(sort, sort.isDefaultDescending); + }, + child: ListView.builder( + controller: scrollController, + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: widget.sortOptions.length, + itemBuilder: (context, index) { + final sort = widget.sortOptions[index]; + final isSelected = _tempSelectedSort?.key == sort.key; - return ListTile( - title: Text(sort.title), - trailing: isSelected - ? Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Direction toggle buttons - SegmentedButton( - showSelectedIcon: false, - segments: const [ - ButtonSegment( - value: false, - icon: Icon(Icons.arrow_upward, size: 16), - ), - ButtonSegment( - value: true, - icon: Icon(Icons.arrow_downward, size: 16), - ), - ], - selected: {_tempDescending}, - onSelectionChanged: (Set selected) { - widget.onSortChanged(sort, selected.first); - }, - ), - ], - ) - : null, - leading: Radio( - value: sort.key, - groupValue: _tempSelectedSort?.key, - onChanged: (value) { + return ListTile( + title: Text(sort.title), + trailing: isSelected + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Direction toggle buttons + SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment( + value: false, + icon: Icon(Icons.arrow_upward, size: 16), + ), + ButtonSegment( + value: true, + icon: Icon(Icons.arrow_downward, size: 16), + ), + ], + selected: {_tempDescending}, + onSelectionChanged: (Set selected) { + widget.onSortChanged(sort, selected.first); + }, + ), + ], + ) + : null, + leading: Radio( + value: sort.key, + toggleable: false, + ), + onTap: () { setState(() { _tempSelectedSort = sort; // Use default direction for newly selected sort @@ -1483,18 +1486,9 @@ class _SortBottomSheetState extends State<_SortBottomSheet> { // Apply sort immediately with default direction widget.onSortChanged(sort, sort.isDefaultDescending); }, - ), - onTap: () { - setState(() { - _tempSelectedSort = sort; - // Use default direction for newly selected sort - _tempDescending = sort.isDefaultDescending; - }); - // Apply sort immediately with default direction - widget.onSortChanged(sort, sort.isDefaultDescending); - }, - ); - }, + ); + }, + ), ), ), ], @@ -1587,6 +1581,7 @@ class _LibraryManagementSheetState extends State<_LibraryManagementSheet> { ); if (selectedItem.requiresConfirmation) { + if (!mounted || !context.mounted) return; final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( @@ -1703,7 +1698,7 @@ class _LibraryManagementSheetState extends State<_LibraryManagementSheet> { Icons.drag_indicator, color: Theme.of( context, - ).textTheme.bodyMedium?.color?.withOpacity(0.5), + ).textTheme.bodyMedium?.color?.withValues(alpha: 0.5), ), ), ), diff --git a/lib/utils/video_player_navigation.dart b/lib/utils/video_player_navigation.dart index 4db81424..22f57523 100644 --- a/lib/utils/video_player_navigation.dart +++ b/lib/utils/video_player_navigation.dart @@ -32,6 +32,9 @@ Future navigateToVideoPlayer( int? selectedMediaIndex, bool usePushReplacement = false, }) async { + // Extract navigator before any async operations + final navigator = Navigator.of(context); + // Load saved media version preference if not explicitly provided int mediaIndex = selectedMediaIndex ?? 0; if (selectedMediaIndex == null) { @@ -60,8 +63,8 @@ Future navigateToVideoPlayer( ); if (usePushReplacement) { - return Navigator.of(context).pushReplacement(route); + return navigator.pushReplacement(route); } else { - return Navigator.push(context, route); + return navigator.push(route); } } diff --git a/lib/widgets/plex_video_controls.dart b/lib/widgets/plex_video_controls.dart index c99ca4c7..e0d3a1cf 100644 --- a/lib/widgets/plex_video_controls.dart +++ b/lib/widgets/plex_video_controls.dart @@ -77,7 +77,6 @@ class _PlexVideoControlsState extends State late final FocusNode _focusNode; KeyboardShortcutsService? _keyboardService; int _seekTimeSmall = 10; // Default, loaded from settings - int _seekTimeLarge = 30; // Default, loaded from settings // Double-tap feedback state bool _showDoubleTapFeedback = false; double _doubleTapFeedbackOpacity = 0.0; @@ -109,7 +108,6 @@ class _PlexVideoControlsState extends State if (mounted) { setState(() { _seekTimeSmall = settingsService.getSeekTimeSmall(); - _seekTimeLarge = settingsService.getSeekTimeLarge(); }); } } @@ -1351,7 +1349,7 @@ class _PlexVideoControlsState extends State selectedSnapshot.data ?? widget.player.state.track; final selectedTrack = currentTrack.audio; - final selectedId = selectedTrack?.id; + final selectedId = selectedTrack.id; return ListView.builder( itemCount: audioTracks.length, @@ -1477,7 +1475,7 @@ class _PlexVideoControlsState extends State selectedSnapshot.data ?? widget.player.state.track; final selectedTrack = currentTrack.subtitle; - final selectedId = selectedTrack?.id; + final selectedId = selectedTrack.id; final isOffSelected = selectedId == 'no'; return ListView.builder( diff --git a/lib/widgets/sort_bottom_sheet.dart b/lib/widgets/sort_bottom_sheet.dart index ac45a4b5..0636b873 100644 --- a/lib/widgets/sort_bottom_sheet.dart +++ b/lib/widgets/sort_bottom_sheet.dart @@ -90,57 +90,60 @@ class _SortBottomSheetState extends State { ), ), Expanded( - child: ListView.builder( - controller: scrollController, - padding: const EdgeInsets.symmetric(vertical: 8), - itemCount: widget.sortOptions.length, - itemBuilder: (context, index) { - final sort = widget.sortOptions[index]; - final isSelected = _currentSort?.key == sort.key; - - return ListTile( - title: Text(sort.title), - trailing: isSelected - ? Row( - mainAxisSize: MainAxisSize.min, - children: [ - SegmentedButton( - showSelectedIcon: false, - segments: const [ - ButtonSegment( - value: false, - icon: Icon(Icons.arrow_upward, size: 16), - ), - ButtonSegment( - value: true, - icon: Icon(Icons.arrow_downward, size: 16), - ), - ], - selected: {_currentDescending}, - onSelectionChanged: (Set newSelection) { - _handleSortChange(sort, newSelection.first); - }, - ), - ], - ) - : null, - leading: Radio( - value: sort, - groupValue: _currentSort, - onChanged: (PlexSort? value) { - if (value != null) { - _handleSortChange( - value, - value.defaultDirection == 'desc', - ); - } - }, - ), - onTap: () { - _handleSortChange(sort, sort.defaultDirection == 'desc'); - }, - ); + child: RadioGroup( + groupValue: _currentSort, + onChanged: (PlexSort? value) { + if (value != null) { + _handleSortChange( + value, + value.defaultDirection == 'desc', + ); + } }, + child: ListView.builder( + controller: scrollController, + padding: const EdgeInsets.symmetric(vertical: 8), + itemCount: widget.sortOptions.length, + itemBuilder: (context, index) { + final sort = widget.sortOptions[index]; + final isSelected = _currentSort?.key == sort.key; + + return ListTile( + title: Text(sort.title), + trailing: isSelected + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment( + value: false, + icon: Icon(Icons.arrow_upward, size: 16), + ), + ButtonSegment( + value: true, + icon: Icon(Icons.arrow_downward, size: 16), + ), + ], + selected: {_currentDescending}, + onSelectionChanged: (Set newSelection) { + _handleSortChange(sort, newSelection.first); + }, + ), + ], + ) + : null, + leading: Radio( + value: sort, + toggleable: false, + ), + onTap: () { + _handleSortChange(sort, sort.defaultDirection == 'desc'); + }, + ); + }, + ), ), ), ],