feat(casting): show stop button when playing a movie

This commit is contained in:
Uruk
2026-05-22 02:45:20 +02:00
parent 674e252641
commit f99ce8210c
2 changed files with 47 additions and 59 deletions

View File

@@ -509,19 +509,18 @@ export default function CastingPlayerScreen() {
/> />
</ScrollView> </ScrollView>
{/* Fixed 4-button control row for episodes - positioned independently */} {/* Fixed control row - positioned independently. Episode-specific
{currentItem.Type === "Episode" && ( buttons are conditional inside; Stop is always available. */}
<CastPlayerEpisodeControls <CastPlayerEpisodeControls
insetBottom={insets.bottom} insetBottom={insets.bottom}
currentItemId={currentItem.Id} currentItemId={currentItem.Id}
episodes={episodes} episodes={episodes}
nextEpisode={nextEpisode} nextEpisode={nextEpisode}
remoteMediaClient={remoteMediaClient} remoteMediaClient={remoteMediaClient}
onPressEpisodes={() => setShowEpisodeList(true)} onPressEpisodes={() => setShowEpisodeList(true)}
loadEpisode={loadEpisode} loadEpisode={loadEpisode}
router={router} router={router}
/> />
)}
{/* Fixed bottom controls area */} {/* Fixed bottom controls area */}
<View <View

View File

@@ -1,6 +1,8 @@
/** /**
* Casting Player Episode Controls * Casting Player Episode Controls
* Fixed 4-button control row for episodes: episode list, previous, next, stop. * Fixed control row: episode list, previous, next, stop.
* Episode-specific buttons (list / previous / next) are conditional;
* Stop is always rendered so movies still get a Stop button.
*/ */
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
@@ -38,6 +40,26 @@ export function CastPlayerEpisodeControls({
loadEpisode, loadEpisode,
router, router,
}: CastPlayerEpisodeControlsProps) { }: CastPlayerEpisodeControlsProps) {
const hasEpisodeList = episodes.length > 0;
const hasPrevious = episodes.findIndex((ep) => ep.Id === currentItemId) > 0;
const hasNext = nextEpisode != null;
// Count of buttons actually rendered (Stop is always rendered).
const buttonCount =
1 + (hasEpisodeList ? 1 : 0) + (hasPrevious ? 1 : 0) + (hasNext ? 1 : 0);
// Each button stretches evenly only when the row holds more than one;
// a lone Stop button keeps its intrinsic size and stays centered.
const buttonStyle = {
...(buttonCount > 1 ? { flex: 1 } : {}),
backgroundColor: "#1a1a1a",
padding: 12,
borderRadius: 12,
flexDirection: "row" as const,
justifyContent: "center" as const,
alignItems: "center" as const,
};
return ( return (
<View <View
style={{ style={{
@@ -52,24 +74,15 @@ export function CastPlayerEpisodeControls({
paddingHorizontal: 20, paddingHorizontal: 20,
}} }}
> >
{/* Episodes button */} {/* Episodes button - only rendered when an episode list exists (not for movies) */}
<Pressable {hasEpisodeList && (
onPress={onPressEpisodes} <Pressable onPress={onPressEpisodes} style={buttonStyle}>
style={{ <Ionicons name='list' size={22} color='white' />
flex: 1, </Pressable>
backgroundColor: "#1a1a1a", )}
padding: 12,
borderRadius: 12,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
}}
>
<Ionicons name='list' size={22} color='white' />
</Pressable>
{/* Previous episode button - only rendered when a previous episode exists */} {/* Previous episode button - only rendered when a previous episode exists */}
{episodes.findIndex((ep) => ep.Id === currentItemId) > 0 && ( {hasPrevious && (
<Pressable <Pressable
onPress={async () => { onPress={async () => {
const currentIndex = episodes.findIndex( const currentIndex = episodes.findIndex(
@@ -79,43 +92,27 @@ export function CastPlayerEpisodeControls({
await loadEpisode(episodes[currentIndex - 1]); await loadEpisode(episodes[currentIndex - 1]);
} }
}} }}
style={{ style={buttonStyle}
flex: 1,
backgroundColor: "#1a1a1a",
padding: 12,
borderRadius: 12,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
}}
> >
<Ionicons name='play-skip-back' size={22} color='white' /> <Ionicons name='play-skip-back' size={22} color='white' />
</Pressable> </Pressable>
)} )}
{/* Next episode button - only rendered when a next episode exists */} {/* Next episode button - only rendered when a next episode exists */}
{nextEpisode && ( {hasNext && (
<Pressable <Pressable
onPress={async () => { onPress={async () => {
if (nextEpisode) { if (nextEpisode) {
await loadEpisode(nextEpisode); await loadEpisode(nextEpisode);
} }
}} }}
style={{ style={buttonStyle}
flex: 1,
backgroundColor: "#1a1a1a",
padding: 12,
borderRadius: 12,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
}}
> >
<Ionicons name='play-skip-forward' size={22} color='white' /> <Ionicons name='play-skip-forward' size={22} color='white' />
</Pressable> </Pressable>
)} )}
{/* Stop playback button - stops media but stays connected to Chromecast */} {/* Stop playback button - always rendered; stops media but stays connected to Chromecast */}
<Pressable <Pressable
onPress={async () => { onPress={async () => {
try { try {
@@ -140,15 +137,7 @@ export function CastPlayerEpisodeControls({
} }
} }
}} }}
style={{ style={buttonStyle}
flex: 1,
backgroundColor: "#1a1a1a",
padding: 12,
borderRadius: 12,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
}}
> >
<Ionicons name='stop-circle' size={22} color='white' /> <Ionicons name='stop-circle' size={22} color='white' />
</Pressable> </Pressable>