mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-14 20:01:58 +08:00
- 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.
159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
import React from 'react';
|
|
import { View, Text, StyleSheet, Modal, FlatList, Pressable, TouchableOpacity } from 'react-native';
|
|
|
|
import usePlayerStore from '@/stores/playerStore';
|
|
import { useState } from 'react';
|
|
|
|
interface Episode {
|
|
title?: string;
|
|
url: string;
|
|
}
|
|
|
|
interface EpisodeSelectionModalProps {}
|
|
|
|
export const EpisodeSelectionModal: React.FC<EpisodeSelectionModalProps> = () => {
|
|
const { showEpisodeModal, episodes, currentEpisodeIndex, playEpisode, setShowEpisodeModal } = usePlayerStore();
|
|
|
|
const [episodeGroupSize] = useState(30);
|
|
const [selectedEpisodeGroup, setSelectedEpisodeGroup] = useState(Math.floor(currentEpisodeIndex / episodeGroupSize));
|
|
|
|
const onSelectEpisode = (index: number) => {
|
|
playEpisode(index);
|
|
setShowEpisodeModal(false);
|
|
};
|
|
|
|
const onClose = () => {
|
|
setShowEpisodeModal(false);
|
|
};
|
|
|
|
return (
|
|
<Modal visible={showEpisodeModal} transparent={true} animationType="slide" onRequestClose={onClose}>
|
|
<View style={styles.modalContainer}>
|
|
<View style={styles.modalContent}>
|
|
<Text style={styles.modalTitle}>选择剧集</Text>
|
|
|
|
{episodes.length > episodeGroupSize && (
|
|
<View style={styles.episodeGroupContainer}>
|
|
{Array.from({ length: Math.ceil(episodes.length / episodeGroupSize) }, (_, groupIndex) => (
|
|
<TouchableOpacity
|
|
key={groupIndex}
|
|
style={[
|
|
styles.episodeGroupButton,
|
|
selectedEpisodeGroup === groupIndex && styles.episodeGroupButtonSelected,
|
|
]}
|
|
onPress={() => setSelectedEpisodeGroup(groupIndex)}
|
|
>
|
|
<Text style={styles.episodeGroupButtonText}>
|
|
{`${groupIndex * episodeGroupSize + 1}-${Math.min(
|
|
(groupIndex + 1) * episodeGroupSize,
|
|
episodes.length
|
|
)}`}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
)}
|
|
<FlatList
|
|
data={episodes.slice(
|
|
selectedEpisodeGroup * episodeGroupSize,
|
|
(selectedEpisodeGroup + 1) * episodeGroupSize
|
|
)}
|
|
numColumns={5}
|
|
keyExtractor={(_, index) => `episode-${selectedEpisodeGroup * episodeGroupSize + index}`}
|
|
renderItem={({ item, index }) => {
|
|
const absoluteIndex = selectedEpisodeGroup * episodeGroupSize + index;
|
|
return (
|
|
<Pressable
|
|
style={({ focused }) => [
|
|
styles.episodeItem,
|
|
currentEpisodeIndex === absoluteIndex && styles.episodeItemSelected,
|
|
focused && styles.focusedButton,
|
|
]}
|
|
onPress={() => onSelectEpisode(absoluteIndex)}
|
|
hasTVPreferredFocus={currentEpisodeIndex === absoluteIndex}
|
|
>
|
|
<Text style={styles.episodeItemText}>{item.title || `第 ${absoluteIndex + 1} 集`}</Text>
|
|
</Pressable>
|
|
);
|
|
}}
|
|
/>
|
|
|
|
<Pressable style={({ focused }) => [styles.closeButton, focused && styles.focusedButton]} onPress={onClose}>
|
|
<Text style={{ color: 'white' }}>关闭</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
modalContainer: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
justifyContent: 'flex-end',
|
|
backgroundColor: 'transparent',
|
|
},
|
|
modalContent: {
|
|
width: 400,
|
|
height: '100%',
|
|
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
|
padding: 20,
|
|
},
|
|
modalTitle: {
|
|
color: 'white',
|
|
marginBottom: 20,
|
|
textAlign: 'center',
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
},
|
|
episodeItem: {
|
|
backgroundColor: '#333',
|
|
paddingVertical: 12,
|
|
borderRadius: 8,
|
|
margin: 4,
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
episodeItemSelected: {
|
|
backgroundColor: '#007bff',
|
|
},
|
|
episodeItemText: {
|
|
color: 'white',
|
|
fontSize: 14,
|
|
},
|
|
episodeGroupContainer: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
justifyContent: 'center',
|
|
marginBottom: 15,
|
|
paddingHorizontal: 10,
|
|
},
|
|
episodeGroupButton: {
|
|
backgroundColor: '#444',
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 6,
|
|
borderRadius: 15,
|
|
margin: 5,
|
|
},
|
|
episodeGroupButtonSelected: {
|
|
backgroundColor: '#007bff',
|
|
},
|
|
episodeGroupButtonText: {
|
|
color: 'white',
|
|
fontSize: 12,
|
|
},
|
|
closeButton: {
|
|
backgroundColor: '#333',
|
|
padding: 15,
|
|
borderRadius: 8,
|
|
alignItems: 'center',
|
|
marginTop: 20,
|
|
},
|
|
focusedButton: {
|
|
backgroundColor: 'rgba(119, 119, 119, 0.9)',
|
|
transform: [{ scale: 1.1 }],
|
|
},
|
|
});
|