/** * Casting Player Poster * Poster image with empty-state fallback, skip intro/credits bar, and buffering overlay. */ import { Ionicons } from "@expo/vector-icons"; import { Image } from "expo-image"; import type { TFunction } from "i18next"; import { ActivityIndicator, Pressable, View } from "react-native"; import { MediaPlayerState, type MediaStatus, type RemoteMediaClient, } from "react-native-google-cast"; import type { useChromecastSegments } from "@/components/chromecast/hooks/useChromecastSegments"; import { Text } from "@/components/common/Text"; type ChromecastSegments = ReturnType; interface CastPlayerPosterProps { /** Poster image URL, or null when unavailable. */ posterUrl: string | null; /** Whether the cast media is currently buffering. */ isBuffering: boolean; /** The current playback segment (intro/credits/etc.), or null. */ currentSegment: ChromecastSegments["currentSegment"]; /** Skip the intro segment. */ skipIntro: ChromecastSegments["skipIntro"]; /** Skip the credits segment. */ skipCredits: ChromecastSegments["skipCredits"]; /** Skip the current generic segment. */ skipSegment: ChromecastSegments["skipSegment"]; /** The remote media client, or null when no session. */ remoteMediaClient: RemoteMediaClient | null; /** Raw Chromecast media status. */ mediaStatus: MediaStatus | null; /** Theme accent color. */ protocolColor: string; /** Translation function. */ t: TFunction; } export function CastPlayerPoster({ posterUrl, isBuffering, currentSegment, skipIntro, skipCredits, skipSegment, remoteMediaClient, mediaStatus, protocolColor, t, }: CastPlayerPosterProps) { return ( {posterUrl ? ( ) : ( )} {/* Skip intro/credits bar at bottom of poster */} {currentSegment && ( { if (!remoteMediaClient) return; try { const seekFn = async (positionMs: number) => { if ( mediaStatus?.playerState === MediaPlayerState.PLAYING || mediaStatus?.playerState === MediaPlayerState.PAUSED ) { await remoteMediaClient.seek({ position: positionMs / 1000, }); } }; if (currentSegment.type === "intro") { await skipIntro(seekFn); } else if (currentSegment.type === "credits") { await skipCredits(seekFn); } else { await skipSegment(seekFn); } } catch (error) { console.error("[Casting Player] Skip error:", error); } }} style={{ position: "absolute", bottom: 0, left: 0, right: 0, backgroundColor: protocolColor, paddingVertical: 12, paddingHorizontal: 16, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 8, }} > {t( `player.skip_${currentSegment.type === "credits" ? "outro" : currentSegment.type}`, )} )} {/* Buffering overlay */} {isBuffering && ( {t("casting_player.buffering")} )} ); }