169 lines
4.2 KiB
TypeScript
169 lines
4.2 KiB
TypeScript
import React from 'react';
|
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import Svg, { Path } from 'react-native-svg';
|
|
import { Starfield } from '../components/Starfield';
|
|
import { ZenCenter } from '../components/ZenCenter';
|
|
import { MarqueeText } from '../components/MarqueeText';
|
|
import { PlayerControls } from '../components/PlayerControls';
|
|
import { theme } from '../theme';
|
|
import { Track, RepeatMode } from '../types';
|
|
|
|
interface PlayerScreenProps {
|
|
currentTrack: Track | null;
|
|
trackCount: number;
|
|
isPlaying: boolean;
|
|
isShuffle: boolean;
|
|
repeatMode: RepeatMode;
|
|
position: number;
|
|
duration: number;
|
|
onPlayPause: () => void;
|
|
onNext: () => void;
|
|
onPrev: () => void;
|
|
onShuffle: () => void;
|
|
onRepeat: () => void;
|
|
onSeek: (position: number) => void;
|
|
onOpenPlaylist: () => void;
|
|
}
|
|
|
|
const ListIcon = () => (
|
|
<Svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke={theme.colors.textSecondary} strokeWidth={2} strokeLinecap="round">
|
|
<Path d="M8 6h13M8 12h13M8 18h13" />
|
|
<Path d="M3 6h.01M3 12h.01M3 18h.01" />
|
|
</Svg>
|
|
);
|
|
|
|
export const PlayerScreen: React.FC<PlayerScreenProps> = ({
|
|
currentTrack,
|
|
trackCount,
|
|
isPlaying,
|
|
isShuffle,
|
|
repeatMode,
|
|
position,
|
|
duration,
|
|
onPlayPause,
|
|
onNext,
|
|
onPrev,
|
|
onShuffle,
|
|
onRepeat,
|
|
onSeek,
|
|
onOpenPlaylist,
|
|
}) => {
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* Starfield background */}
|
|
<Starfield />
|
|
|
|
<SafeAreaView style={styles.safeContainer} edges={['top', 'bottom']}>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<Text style={styles.headerTitle}>观空禅音</Text>
|
|
<Text style={styles.headerCount}>{trackCount} 首</Text>
|
|
</View>
|
|
|
|
{/* Zen center */}
|
|
<View style={styles.zenCenterContainer}>
|
|
<ZenCenter isPlaying={isPlaying} />
|
|
</View>
|
|
|
|
{/* Track info */}
|
|
<View style={styles.trackInfo}>
|
|
<MarqueeText
|
|
text={currentTrack?.title || '未选择歌曲'}
|
|
enabled={!!currentTrack?.title}
|
|
style={styles.trackTitle}
|
|
containerStyle={styles.trackTitleWrap}
|
|
/>
|
|
<Text style={styles.trackArtist} numberOfLines={1}>
|
|
{currentTrack?.artist || '—'}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Spacer */}
|
|
<View style={styles.spacer} />
|
|
|
|
{/* Player controls */}
|
|
<PlayerControls
|
|
isPlaying={isPlaying}
|
|
isShuffle={isShuffle}
|
|
repeatMode={repeatMode}
|
|
position={position}
|
|
duration={duration}
|
|
onPlayPause={onPlayPause}
|
|
onNext={onNext}
|
|
onPrev={onPrev}
|
|
onShuffle={onShuffle}
|
|
onRepeat={onRepeat}
|
|
onSeek={onSeek}
|
|
/>
|
|
|
|
{/* Playlist button */}
|
|
<View style={styles.playlistButtonContainer}>
|
|
<TouchableOpacity style={styles.playlistButton} onPress={onOpenPlaylist}>
|
|
<ListIcon />
|
|
</TouchableOpacity>
|
|
</View>
|
|
</SafeAreaView>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: theme.colors.background,
|
|
},
|
|
safeContainer: {
|
|
flex: 1,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingHorizontal: theme.spacing.md,
|
|
paddingVertical: theme.spacing.sm,
|
|
},
|
|
headerTitle: {
|
|
fontSize: 20,
|
|
fontWeight: '600',
|
|
letterSpacing: 0.06,
|
|
color: theme.colors.text,
|
|
},
|
|
headerCount: {
|
|
fontSize: 13,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
zenCenterContainer: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
trackInfo: {
|
|
alignItems: 'center',
|
|
paddingHorizontal: theme.spacing.md,
|
|
},
|
|
trackTitleWrap: {
|
|
width: '100%',
|
|
},
|
|
trackTitle: {
|
|
fontSize: 17,
|
|
fontWeight: '600',
|
|
color: theme.colors.text,
|
|
marginBottom: 3,
|
|
},
|
|
trackArtist: {
|
|
fontSize: 13,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
spacer: {
|
|
height: theme.spacing.md,
|
|
},
|
|
playlistButtonContainer: {
|
|
alignItems: 'center',
|
|
paddingBottom: theme.spacing.sm,
|
|
},
|
|
playlistButton: {
|
|
padding: theme.spacing.sm,
|
|
},
|
|
});
|