From 02df2477d82ea60c15f374040cc5ff31c2eb1efc Mon Sep 17 00:00:00 2001 From: Uruk Date: Fri, 22 May 2026 00:57:55 +0200 Subject: [PATCH] refactor(casting): extract CastPlayerHeader and CastPlayerTitle --- app/(auth)/casting-player.tsx | 122 +++--------------- .../casting/player/CastPlayerHeader.tsx | 94 ++++++++++++++ components/casting/player/CastPlayerTitle.tsx | 72 +++++++++++ 3 files changed, 182 insertions(+), 106 deletions(-) create mode 100644 components/casting/player/CastPlayerHeader.tsx create mode 100644 components/casting/player/CastPlayerTitle.tsx diff --git a/app/(auth)/casting-player.tsx b/app/(auth)/casting-player.tsx index 2a7ddaa73..d1735bd4d 100644 --- a/app/(auth)/casting-player.tsx +++ b/app/(auth)/casting-player.tsx @@ -36,6 +36,8 @@ import Animated, { } from "react-native-reanimated"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { BITRATES } from "@/components/BitrateSelector"; +import { CastPlayerHeader } from "@/components/casting/player/CastPlayerHeader"; +import { CastPlayerTitle } from "@/components/casting/player/CastPlayerTitle"; import { ChromecastDeviceSheet } from "@/components/chromecast/ChromecastDeviceSheet"; import { ChromecastEpisodeList } from "@/components/chromecast/ChromecastEpisodeList"; import { ChromecastSettingsMenu } from "@/components/chromecast/ChromecastSettingsMenu"; @@ -53,7 +55,6 @@ import { formatTime, formatTrickplayTime, getPosterUrl, - truncateTitle, } from "@/utils/casting/helpers"; import { resolveSelection } from "@/utils/casting/selection"; import type { CastSelection } from "@/utils/casting/types"; @@ -667,113 +668,22 @@ export default function CastingPlayerScreen() { ]} > {/* Header - Fixed at top */} - - - - - - {/* Connection indicator */} - setShowDeviceSheet(true)} - style={{ - flexDirection: "row", - alignItems: "center", - gap: 6, - paddingHorizontal: 12, - paddingVertical: 6, - backgroundColor: "#1a1a1a", - borderRadius: 16, - }} - > - - - {currentDevice || t("casting_player.unknown_device")} - - - - setShowSettings(true)} - style={{ padding: 8, marginRight: -8 }} - > - - - + setShowDeviceSheet(true)} + onPressSettings={() => setShowSettings(true)} + /> {/* Title Area */} - - {/* Title */} - - {truncateTitle( - currentItem.Name || t("casting_player.unknown"), - 50, - )} - - - {/* Grey episode/season info */} - {currentItem.Type === "Episode" && - currentItem.ParentIndexNumber !== undefined && - currentItem.IndexNumber !== undefined && ( - - {t("casting_player.season_episode_format", { - season: currentItem.ParentIndexNumber, - episode: currentItem.IndexNumber, - })} - - )} - + {/* Scrollable content area */} void; + /** Open the device sheet (connection indicator press). */ + onPressConnectionIndicator: () => void; + /** Open the settings menu. */ + onPressSettings: () => void; +} + +export function CastPlayerHeader({ + insetTop, + protocolColor, + currentDevice, + t, + onDismiss, + onPressConnectionIndicator, + onPressSettings, +}: CastPlayerHeaderProps) { + return ( + + + + + + {/* Connection indicator */} + + + + {currentDevice || t("casting_player.unknown_device")} + + + + + + + + ); +} diff --git a/components/casting/player/CastPlayerTitle.tsx b/components/casting/player/CastPlayerTitle.tsx new file mode 100644 index 000000000..fcf9ff94f --- /dev/null +++ b/components/casting/player/CastPlayerTitle.tsx @@ -0,0 +1,72 @@ +/** + * Casting Player Title Area + * Fixed title bar: item title and optional grey episode/season info. + */ + +import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; +import type { TFunction } from "i18next"; +import { View } from "react-native"; +import { Text } from "@/components/common/Text"; +import { truncateTitle } from "@/utils/casting/helpers"; + +interface CastPlayerTitleProps { + /** Top safe-area inset, used to offset the fixed title area. */ + insetTop: number; + /** The currently playing item. */ + currentItem: BaseItemDto; + /** Translation function. */ + t: TFunction; +} + +export function CastPlayerTitle({ + insetTop, + currentItem, + t, +}: CastPlayerTitleProps) { + return ( + + {/* Title */} + + {truncateTitle(currentItem.Name || t("casting_player.unknown"), 50)} + + + {/* Grey episode/season info */} + {currentItem.Type === "Episode" && + currentItem.ParentIndexNumber !== undefined && + currentItem.IndexNumber !== undefined && ( + + {t("casting_player.season_episode_format", { + season: currentItem.ParentIndexNumber, + episode: currentItem.IndexNumber, + })} + + )} + + ); +}