import { useRouter } from 'expo-router'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, Text, View, type LayoutChangeEvent } from 'react-native'; import { MusicShareSheet } from '../features/social/Social'; import { ListeningBanner } from '../features/social/Listening'; import { trackShareUrl } from '@pipu/core'; import { useSessionCtx, useUser } from '../lib/session'; import { player } from '../player/controller'; import { usePlayerState } from '../player/hooks'; import { useColors } from '../theme'; import { DownloadButton } from '../ui/downloads'; import { Cover, Icon, Screen, formatDuration } from '../ui/kit'; import { WaveOrb } from '../ui/wave/WaveOrb'; /** * Полноэкранный плеер: вид `cover` (крупная обложка) или `wave` (визуализация волны в цветах обложки) — по профилю; * прогресс с перемоткой, лайк / дизлайк, скачать, управление. */ export default function PlayerScreen() { const colors = useColors(); const { t } = useTranslation(); const router = useRouter(); const st = usePlayerState(); const locked = st.listening?.locked === true; const prefs = useUser().ui_prefs; const { server } = useSessionCtx(); const [sharing, setSharing] = useState(false); const [barWidth, setBarWidth] = useState(1); const track = st.track; if (!track) { return ( router.back()} style={{ padding: 16 }} accessibilityRole="button" accessibilityLabel={t('mobile.close_player')}> ); } const duration = st.durationMs || track.duration_ms; const progress = duration ? Math.min(st.positionMs / duration, 1) : 0; const react = (r: 'like' | 'dislike') => void player.react(track, track.reaction === r ? null : r).catch(() => undefined); const share = () => setSharing(true); return ( {sharing && server && setSharing(false)} />} router.back()} hitSlop={10} accessibilityRole="button" accessibilityLabel={t('mobile.close_player')}> {st.wave ? t('wave.title') : t('mobile.now_playing')} void share()} disabled={!server || sharing} hitSlop={10} accessibilityRole="button" accessibilityLabel={t('player.share')} accessibilityState={{ disabled: !server || sharing }}> {prefs.mobile_player === 'wave' ? ( ) : ( )} {track.title} {track.artists.map((a) => a.name).join(', ')} router.push({ pathname: '/playlists', params: { track: track.id } })} accessibilityRole="button" accessibilityLabel={t('playlists.add')} style={{ flexDirection: 'row', alignItems: 'center', gap: 8, minHeight: 44, alignSelf: 'flex-start' }}> {t('playlists.add')} setBarWidth(e.nativeEvent.layout.width || 1)} disabled={locked} onPress={(e) => player.seek((e.nativeEvent.locationX / barWidth) * duration)} accessibilityRole="adjustable" accessibilityLabel={t('player.progress')} accessibilityValue={{ min: 0, max: Math.round(duration / 1000), now: Math.round(st.positionMs / 1000) }} accessibilityActions={[{ name: 'increment' }, { name: 'decrement' }]} onAccessibilityAction={(e) => player.seek(st.positionMs + (e.nativeEvent.actionName === 'increment' ? 10_000 : -10_000))} style={{ height: 24, justifyContent: 'center' }} > {formatDuration(st.positionMs)} {formatDuration(duration)} react('dislike')} hitSlop={10} accessibilityRole="button" accessibilityLabel={t(track.reaction === 'dislike' ? 'player.undislike' : 'player.dislike')}> void player.prev()} hitSlop={10} accessibilityRole="button" accessibilityLabel={t('player.prev')}> player.toggle()} accessibilityRole="button" accessibilityLabel={t(st.playing ? 'player.pause' : 'player.play')} style={{ width: 72, height: 72, borderRadius: 36, backgroundColor: colors.text, alignItems: 'center', justifyContent: 'center' }} > player.next()} hitSlop={10} accessibilityRole="button" accessibilityLabel={t('player.next')}> react('like')} hitSlop={10} accessibilityRole="button" accessibilityLabel={t(track.reaction === 'like' ? 'player.unlike' : 'player.like')}> ); }