Files
OrionTV/app/play.tsx
zimplexing 08e24dd748 Refactor components to use Zustand for state management
- Updated EpisodeSelectionModal to utilize Zustand for episode selection state.
- Refactored PlayerControls to manage playback state and controls using Zustand.
- Simplified SettingsModal to handle settings state with Zustand.
- Introduced homeStore for managing home screen categories and content data.
- Created playerStore for managing video playback and episode details.
- Added settingsStore for managing API settings and modal visibility.
- Updated package.json to include Zustand as a dependency.
- Cleaned up code formatting and improved readability across components.
2025-07-06 20:45:42 +08:00

117 lines
3.4 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { View, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
import { Video, ResizeMode } from 'expo-av';
import { useKeepAwake } from 'expo-keep-awake';
import { ThemedView } from '@/components/ThemedView';
import { PlayerControls } from '@/components/PlayerControls';
import { EpisodeSelectionModal } from '@/components/EpisodeSelectionModal';
import { NextEpisodeOverlay } from '@/components/NextEpisodeOverlay';
import { LoadingOverlay } from '@/components/LoadingOverlay';
import usePlayerStore from '@/stores/playerStore';
import { useTVRemoteHandler } from '@/hooks/useTVRemoteHandler';
export default function PlayScreen() {
const videoRef = useRef<Video>(null);
useKeepAwake();
const { source, id, episodeIndex } = useLocalSearchParams<{ source: string; id: string; episodeIndex: string }>();
const {
detail,
episodes,
currentEpisodeIndex,
isLoading,
showControls,
showEpisodeModal,
showNextEpisodeOverlay,
setVideoRef,
loadVideo,
playEpisode,
togglePlayPause,
seek,
handlePlaybackStatusUpdate,
setShowControls,
setShowEpisodeModal,
setShowNextEpisodeOverlay,
reset,
} = usePlayerStore();
useEffect(() => {
setVideoRef(videoRef);
if (source && id) {
loadVideo(source, id, parseInt(episodeIndex || '0', 10));
}
return () => {
reset(); // Reset state when component unmounts
};
}, [source, id, episodeIndex, setVideoRef, loadVideo, reset]);
const { setCurrentFocus } = useTVRemoteHandler({
showControls,
setShowControls,
showEpisodeModal,
onPlayPause: togglePlayPause,
onSeek: seek,
onShowEpisodes: () => setShowEpisodeModal(true),
onPlayNextEpisode: () => {
if (currentEpisodeIndex < episodes.length - 1) {
playEpisode(currentEpisodeIndex + 1);
}
},
});
if (!detail && isLoading) {
return (
<ThemedView style={[styles.container, styles.centered]}>
<ActivityIndicator size="large" color="#fff" />
</ThemedView>
);
}
const currentEpisode = episodes[currentEpisodeIndex];
return (
<ThemedView style={styles.container}>
<TouchableOpacity
activeOpacity={1}
style={styles.videoContainer}
onPress={() => {
setShowControls(!showControls);
setCurrentFocus(null);
}}
>
<Video
ref={videoRef}
style={styles.videoPlayer}
source={{ uri: currentEpisode?.url }}
resizeMode={ResizeMode.CONTAIN}
onPlaybackStatusUpdate={handlePlaybackStatusUpdate}
onLoad={() => usePlayerStore.setState({ isLoading: false })}
onLoadStart={() => usePlayerStore.setState({ isLoading: true })}
useNativeControls={false}
shouldPlay
/>
{showControls && <PlayerControls />}
<LoadingOverlay visible={isLoading} />
<NextEpisodeOverlay visible={showNextEpisodeOverlay} onCancel={() => setShowNextEpisodeOverlay(false)} />
</TouchableOpacity>
<EpisodeSelectionModal />
</ThemedView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'black' },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
videoContainer: {
...StyleSheet.absoluteFillObject,
},
videoPlayer: {
...StyleSheet.absoluteFillObject,
},
});