import 'package:flutter/material.dart'; import '../media/media_item.dart'; import '../media/media_kind.dart'; import '../media/media_playlist.dart'; import '../screens/collection_detail_screen.dart'; import '../screens/main_screen.dart'; import '../screens/media_detail_screen.dart'; import '../screens/playlist/playlist_detail_screen.dart'; import '../utils/global_key_utils.dart'; import 'plex_library_section_helpers.dart'; import 'video_player_navigation.dart'; /// Result of media navigation indicating what action was taken enum MediaNavigationResult { /// Navigation completed successfully navigated, /// Navigation completed, parent list should be refreshed (e.g., collection deleted) listRefreshNeeded, /// Item type not supported (e.g., music content) unsupported, /// Item is a library section — navigated to that library librarySelected, } /// Navigates to the appropriate screen based on the item type. /// /// Accepts a [MediaItem] or a [MediaPlaylist] (typed as [Object] because Dart /// has no nominal union type). /// /// For episodes, starts playback directly via video player. /// For movies, starts playback directly if [playDirectly] is true, otherwise /// navigates to media detail screen. /// For seasons, navigates to season detail screen. /// For playlists, navigates to playlist detail screen. /// For collections, navigates to collection detail screen. /// For other types (shows), navigates to media detail screen. /// For music types (artist, album, track), returns [MediaNavigationResult.unsupported]. /// /// The [onRefresh] callback is invoked with the item's id after returning from /// the detail screen, allowing the caller to refresh state. /// /// Set [isOffline] to true for downloaded content without server access. /// /// Set [playDirectly] to true to play movies immediately (e.g., from continue watching). /// /// Returns a [MediaNavigationResult] indicating what action was taken: /// - [MediaNavigationResult.navigated]: Navigation completed, item refresh handled /// - [MediaNavigationResult.listRefreshNeeded]: Caller should refresh entire list /// - [MediaNavigationResult.unsupported]: Item type not supported, caller should handle Future navigateToMediaItem( BuildContext context, Object item, { void Function(String)? onRefresh, bool isOffline = false, bool playDirectly = false, }) async { if (item is MediaPlaylist) { await Navigator.push(context, MaterialPageRoute(builder: (context) => PlaylistDetailScreen(playlist: item))); return MediaNavigationResult.navigated; } if (item is! MediaItem) { return MediaNavigationResult.unsupported; } final mi = item; // Handle library section items (shared whole-library entries) — Plex-only; // [PlexLibrarySection.isLibrarySection] reads the stashed `key` from `raw`. if (mi.isLibrarySection) { final sectionKey = mi.librarySectionKey; if (sectionKey != null && mi.serverId != null) { final libraryGlobalKey = buildGlobalKey(mi.serverId!, sectionKey); MainScreenFocusScope.of(context)?.selectLibrary?.call(libraryGlobalKey); return MediaNavigationResult.librarySelected; } return MediaNavigationResult.unsupported; } switch (mi.kind) { case MediaKind.collection: final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => CollectionDetailScreen(collection: mi)), ); // If collection was deleted, signal that list refresh is needed if (result == true) { return MediaNavigationResult.listRefreshNeeded; } return MediaNavigationResult.navigated; case MediaKind.artist: case MediaKind.album: case MediaKind.track: // Music types not supported return MediaNavigationResult.unsupported; case MediaKind.clip: case MediaKind.episode: final result = await navigateToVideoPlayer(context, metadata: mi, isOffline: isOffline); if (result == true) { onRefresh?.call(mi.id); } return MediaNavigationResult.navigated; case MediaKind.movie: if (playDirectly) { final result = await navigateToVideoPlayer(context, metadata: mi, isOffline: isOffline); if (result == true) { onRefresh?.call(mi.id); } return MediaNavigationResult.navigated; } return _showDetail(context, mi, isOffline, onRefresh); case MediaKind.season: if (mi.parentId != null) { final showStub = MediaItem( id: mi.parentId!, backend: mi.backend, kind: MediaKind.show, title: mi.grandparentTitle ?? mi.parentTitle ?? mi.displayTitle, thumbPath: mi.grandparentThumbPath ?? mi.parentThumbPath, artPath: mi.grandparentArtPath, libraryId: mi.libraryId, libraryTitle: mi.libraryTitle, serverId: mi.serverId, serverName: mi.serverName, ); final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => MediaDetailScreen(metadata: showStub, isOffline: isOffline, initialSeasonIndex: mi.index), ), ); if (result == true) { onRefresh?.call(mi.id); } return MediaNavigationResult.navigated; } return _showDetail(context, mi, isOffline, onRefresh); default: return _showDetail(context, mi, isOffline, onRefresh); } } Future _showDetail( BuildContext context, MediaItem mi, bool isOffline, void Function(String)? onRefresh, ) async { final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => MediaDetailScreen(metadata: mi, isOffline: isOffline), ), ); if (result == true) { onRefresh?.call(mi.id); } return MediaNavigationResult.navigated; }