diff --git a/lib/screens/auth/plex_pin_auth_flow.dart b/lib/screens/auth/plex_pin_auth_flow.dart index e2d5adbfa..30cd2450d 100644 --- a/lib/screens/auth/plex_pin_auth_flow.dart +++ b/lib/screens/auth/plex_pin_auth_flow.dart @@ -42,14 +42,6 @@ class PlexPinAuthFlow extends StatefulWidget { /// for a [PlexAccountConnection] (account label + servers list). final Future Function(String token) onTokenReceived; - /// QR size on mobile / narrow layouts. - final double mobileQrSize; - - /// QR size on desktop / wide layouts (where the auth screen has more - /// horizontal room). The two-column login screen uses 300; the bottom-sheet - /// add-account screen uses 200. - final double desktopQrSize; - /// When `true` and running on TV, auto-start the QR flow on first build so /// the user doesn't have to navigate to the QR button with the remote. final bool autoStartQrOnTV; @@ -57,11 +49,6 @@ class PlexPinAuthFlow extends StatefulWidget { /// Test seam for rendering the initial actions without platform services. final bool initializeService; - /// Override the QR-vs-browser default before any user interaction. Useful - /// for callers that want to force one mode (the add-account screen - /// auto-starts QR on TV; the legacy login screen offers both). - final bool? initialUseQr; - /// Optional builder for the initial action buttons. The default (`null`) /// shows two buttons — "Sign in with Plex" (browser) and "Show QR Code". /// Pass a custom builder when the parent wants to integrate the buttons @@ -76,11 +63,8 @@ class PlexPinAuthFlow extends StatefulWidget { const PlexPinAuthFlow({ super.key, required this.onTokenReceived, - this.mobileQrSize = 200, - this.desktopQrSize = 300, this.autoStartQrOnTV = true, this.initializeService = true, - this.initialUseQr, this.initialButtonsBuilder, this.serviceFactory, }); @@ -100,9 +84,7 @@ class _PlexPinAuthFlowState extends State { @override void initState() { super.initState(); - _useQr = - widget.initialUseQr ?? - (PlatformDetector.isTV() || plexSignInRequiresInAppQr(isAutomotive: PlatformDetector.isAutomotive())); + _useQr = PlatformDetector.isTV() || plexSignInRequiresInAppQr(isAutomotive: PlatformDetector.isAutomotive()); if (widget.initializeService) unawaited(_initService()); } @@ -231,7 +213,7 @@ class _PlexPinAuthFlowState extends State { if (_isPolling) { final isDesktop = MediaQuery.sizeOf(context).width > 700; if (_useQr && _qrAuthUrl != null) { - return _buildQr(theme, isDesktop ? widget.desktopQrSize : widget.mobileQrSize); + return _buildQr(theme, isDesktop ? 300 : 200); } return _buildBrowserWaiting(theme); } diff --git a/lib/screens/downloads/downloads_screen.dart b/lib/screens/downloads/downloads_screen.dart index 5bb485f23..64d753c85 100644 --- a/lib/screens/downloads/downloads_screen.dart +++ b/lib/screens/downloads/downloads_screen.dart @@ -255,7 +255,7 @@ class DownloadsScreenState extends State } } -enum DownloadType { manage, tvShows, movies } +enum DownloadType { tvShows, movies } /// Grid content for TV Shows and Movies tabs class _DownloadsGridContent extends StatefulWidget { @@ -388,12 +388,11 @@ class _DownloadedMusicContentState extends State<_DownloadedMusicContent> bool suppressAutoFocusOf(_DownloadedMusicContent widget) => widget.suppressAutoFocus; Future _playAlbumFrom(List albumTracks, MediaItem track) async { - final album = track.parentId; await playTracks( context, tracks: albumTracks, startTrack: track, - playContext: MusicPlayContext(id: album, title: track.albumTitle ?? '', kind: MusicPlayContextKind.album), + playContext: MusicPlayContext(title: track.albumTitle ?? '', kind: MusicPlayContextKind.album), ); } diff --git a/lib/screens/libraries/libraries_screen.dart b/lib/screens/libraries/libraries_screen.dart index 07afce1a9..80d5271df 100644 --- a/lib/screens/libraries/libraries_screen.dart +++ b/lib/screens/libraries/libraries_screen.dart @@ -59,7 +59,6 @@ class _LibrariesScreenState extends State String? _errorMessage; String? _selectedLibraryGlobalKey; - bool _isInitialLoad = true; /// Flag to prevent onTabChanged from focusing when we're programmatically changing tabs bool _isRestoringTab = false; @@ -441,11 +440,6 @@ class _LibrariesScreenState extends State }); } - // Mark that initial load is complete - if (_isInitialLoad) { - _isInitialLoad = false; - } - // Save selected library key and restore saved tab (async — safe after state is consistent) final storage = await StorageService.getInstance(); if (!mounted || _selectedLibraryGlobalKey != libraryGlobalKey) return; diff --git a/lib/screens/libraries/tabs/library_recommended_tab.dart b/lib/screens/libraries/tabs/library_recommended_tab.dart index 6bf04b671..97a61bf06 100644 --- a/lib/screens/libraries/tabs/library_recommended_tab.dart +++ b/lib/screens/libraries/tabs/library_recommended_tab.dart @@ -289,7 +289,7 @@ class _LibraryRecommendedTabState extends BaseLibraryTabState on State { /// Channel list used for lookups and passed into the playback navigator. List get liveTvChannels; - /// Look up a channel by identifier or key. Returns null if no match. - LiveTvChannel? findChannel(String? channelIdentifier) { - if (channelIdentifier == null) return null; - return liveTvChannels.where((ch) { - return ch.identifier == channelIdentifier || ch.key == channelIdentifier; - }).firstOrNull; - } - LiveTvChannel? findChannelForProgram(LiveTvProgram program) { return liveTvChannels.where((channel) => liveTvProgramMatchesChannel(program, channel)).firstOrNull; } diff --git a/lib/screens/playlist/playlist_detail_screen.dart b/lib/screens/playlist/playlist_detail_screen.dart index 7a881e4d0..fb033f97e 100644 --- a/lib/screens/playlist/playlist_detail_screen.dart +++ b/lib/screens/playlist/playlist_detail_screen.dart @@ -693,7 +693,7 @@ class _PlaylistDetailScreenState extends BaseMediaListDetailScreen with ContextMenuTap key: contextMenuKey, item: item, onRefresh: widget.onRefresh, - onTap: widget.onTap, child: Card( margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), color: cardColor, diff --git a/lib/screens/profile/pin_entry_dialog.dart b/lib/screens/profile/pin_entry_dialog.dart index 5eee1f671..c50c34488 100644 --- a/lib/screens/profile/pin_entry_dialog.dart +++ b/lib/screens/profile/pin_entry_dialog.dart @@ -484,7 +484,7 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin { Widget _buildKeypadLayout(BuildContext context) { return Column( mainAxisSize: .min, - children: [_buildDigitRow(context, obscureDigits: true), const SizedBox(height: 18), _buildKeypad(context)], + children: [_buildDigitRow(context), const SizedBox(height: 18), _buildKeypad(context)], ); } @@ -553,7 +553,7 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin { ); } - Widget _buildDigitRow(BuildContext _, {bool obscureDigits = false}) { + Widget _buildDigitRow(BuildContext _) { return Row( mainAxisSize: .min, mainAxisAlignment: .center, @@ -563,7 +563,6 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin { _DigitBox( digit: _digits[i], isActive: (widget.isMobile ? _mobileFocusNode.hasFocus : true) && _activeIndex == i, - obscureDigit: obscureDigits, ), ], ], @@ -602,7 +601,7 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin { ), ), ), - _buildDigitRow(context, obscureDigits: true), + _buildDigitRow(context), ], ), ); @@ -612,9 +611,8 @@ class _TvPinInputState extends State<_TvPinInput> with ControllerDisposerMixin { class _DigitBox extends StatelessWidget { final int? digit; final bool isActive; - final bool obscureDigit; - const _DigitBox({required this.digit, required this.isActive, this.obscureDigit = false}); + const _DigitBox({required this.digit, required this.isActive}); @override Widget build(BuildContext context) { @@ -640,7 +638,7 @@ class _DigitBox extends StatelessWidget { color: isActive ? focusColor.withValues(alpha: 0.08) : Colors.transparent, ), child: Text( - digit != null ? (obscureDigit || !isActive ? '•' : digit.toString()) : '–', + digit != null ? '•' : '–', style: theme.textTheme.headlineSmall?.copyWith( fontWeight: .bold, color: digit != null @@ -664,16 +662,9 @@ class _PinKey { const _PinKey.backspace() : type = _PinKeyType.backspace, digit = null; const _PinKey.close() : type = _PinKeyType.close, digit = null; - String get label { - switch (type) { - case _PinKeyType.digit: - return digit.toString(); - case _PinKeyType.backspace: - return t.common.delete; - case _PinKeyType.close: - return t.common.cancel; - } - } + /// Only evaluated for digit keys — icon keys short-circuit on [icon] in + /// [_TvPinInputState._buildKeyContent]. + String get label => digit.toString(); IconData? get icon { switch (type) { diff --git a/lib/screens/profile/profile_name_field.dart b/lib/screens/profile/profile_name_field.dart index 04096d7d0..dcc0fa5d3 100644 --- a/lib/screens/profile/profile_name_field.dart +++ b/lib/screens/profile/profile_name_field.dart @@ -14,10 +14,7 @@ class ProfileNameField extends StatelessWidget { this.hintText, this.trailing, this.onChanged, - this.autofocus = false, - this.onNavigateUp, this.onNavigateDown, - this.onNavigateLeft, this.onNavigateRight, }); @@ -26,10 +23,7 @@ class ProfileNameField extends StatelessWidget { final String? hintText; final Widget? trailing; final VoidCallback? onChanged; - final bool autofocus; - final VoidCallback? onNavigateUp; final VoidCallback? onNavigateDown; - final VoidCallback? onNavigateLeft; final VoidCallback? onNavigateRight; @override @@ -37,13 +31,11 @@ class ProfileNameField extends StatelessWidget { final field = FocusableTextField( controller: controller, focusNode: focusNode, - autofocus: autofocus, textInputAction: TextInputAction.done, decoration: InputDecoration(hintText: hintText), onChanged: (_) => onChanged?.call(), - onNavigateUp: onNavigateUp ?? () => FocusScope.of(context).previousFocus(), + onNavigateUp: () => FocusScope.of(context).previousFocus(), onNavigateDown: onNavigateDown ?? () => FocusScope.of(context).nextFocus(), - onNavigateLeft: onNavigateLeft, onNavigateRight: onNavigateRight, ); if (trailing == null) return field; diff --git a/lib/screens/settings/add_jellyfin_screen.dart b/lib/screens/settings/add_jellyfin_screen.dart index 67ef33522..1bae85f25 100644 --- a/lib/screens/settings/add_jellyfin_screen.dart +++ b/lib/screens/settings/add_jellyfin_screen.dart @@ -178,11 +178,6 @@ class _AddJellyfinScreenState extends State with AsyncFormSta } void _syncDiscoveredServerFocusNodes(List servers) { - final ids = servers.map((server) => server.id).toSet(); - final removed = _discoveredServerFocusNodes.keys.where((id) => !ids.contains(id)).toList(growable: false); - for (final id in removed) { - _discoveredServerFocusNodes.remove(id)?.dispose(); - } for (final server in servers) { _discoveredServerFocusNodes.putIfAbsent( server.id, diff --git a/lib/widgets/episode_card.dart b/lib/widgets/episode_card.dart index dc2eb1f1e..38bda0c7f 100644 --- a/lib/widgets/episode_card.dart +++ b/lib/widgets/episode_card.dart @@ -146,7 +146,6 @@ class _EpisodeCardState extends State with ContextMenuTapMixin with ContextMenuTapMixin _handleTap(context, item), isInContinueWatching: widget.isInContinueWatching, collectionId: widget.collectionId, child: cardWidget, @@ -1249,7 +1248,7 @@ Color? _parseCatalogAccent(String? value) { String? _catalogRankBadge(CatalogItem item) { String? allTimeLabel; for (final rank in item.ranks ?? const []) { - final contextual = !rank.allTime || rank.scope == CatalogRankScope.seasonal; + final contextual = !rank.allTime; if (contextual) { final season = rank.season; final year = rank.year; @@ -1262,8 +1261,8 @@ String? _catalogRankBadge(CatalogItem item) { return t.explore.badge.rankSeasonal(n: rank.rank, season: window); } - // Not contextual means an all-time rank with a non-seasonal scope, so - // rankLabel takes its all-time scope-switch path. + // Not contextual means an all-time rank, so rankLabel takes its + // scope-switch path. allTimeLabel ??= rankLabel(rank); } return allTimeLabel; diff --git a/lib/widgets/media_context_menu.dart b/lib/widgets/media_context_menu.dart index 51aceee5d..d9513ebbc 100644 --- a/lib/widgets/media_context_menu.dart +++ b/lib/widgets/media_context_menu.dart @@ -154,7 +154,6 @@ class MediaContextMenu extends StatefulWidget { final void Function(MediaItem source)? onRefresh; final VoidCallback? onRemoveFromContinueWatching; final VoidCallback? onListRefresh; - final VoidCallback? onTap; /// Plays the item's trailer. When non-null a "Play trailer" item is added to /// the menu. Only the detail screen passes this (it resolves the trailer from @@ -175,7 +174,6 @@ class MediaContextMenu extends StatefulWidget { this.onRefresh, this.onRemoveFromContinueWatching, this.onListRefresh, - this.onTap, this.onPlayTrailer, required this.child, this.isInContinueWatching = false, @@ -1254,7 +1252,6 @@ class MediaContextMenuState extends State { context, fetch: () => _musicTracksForItem(item), playContext: MusicPlayContext( - id: item.id, title: item.displayTitle, kind: item.kind == MediaKind.artist ? MusicPlayContextKind.artist : MusicPlayContextKind.album, ), diff --git a/lib/widgets/music/music_detail_header.dart b/lib/widgets/music/music_detail_header.dart index b09bd0f67..94d216f9d 100644 --- a/lib/widgets/music/music_detail_header.dart +++ b/lib/widgets/music/music_detail_header.dart @@ -16,7 +16,6 @@ class MusicDetailHeader extends StatelessWidget { required this.compactArtworkSize, required this.compactArtworkSpacing, this.compactBottomSpacing = 0, - this.wideArtworkSize = 180, this.wideAlignment = CrossAxisAlignment.center, }); @@ -26,7 +25,6 @@ class MusicDetailHeader extends StatelessWidget { final double compactArtworkSize; final double compactArtworkSpacing; final double compactBottomSpacing; - final double wideArtworkSize; final CrossAxisAlignment wideAlignment; @override @@ -51,7 +49,7 @@ class MusicDetailHeader extends StatelessWidget { return Row( crossAxisAlignment: wideAlignment, children: [ - artworkBuilder(wideArtworkSize), + artworkBuilder(180), const SizedBox(width: 24), Expanded( child: Column( diff --git a/lib/widgets/music/track_row.dart b/lib/widgets/music/track_row.dart index aba4bcf52..269333320 100644 --- a/lib/widgets/music/track_row.dart +++ b/lib/widgets/music/track_row.dart @@ -291,13 +291,7 @@ class _TrackRowState extends State with ContextMenuTapMixin, ); if (!withContextMenu) return row; - return MediaContextMenu( - key: contextMenuKey, - item: widget.item, - onRefresh: widget.onRefresh, - onTap: widget.onTap, - child: row, - ); + return MediaContextMenu(key: contextMenuKey, item: widget.item, onRefresh: widget.onRefresh, child: row); } }