1import React, {memo, useEffect, useState} from 'react'; 2import {Keyboard, StyleSheet, View} from 'react-native'; 3import rpx from '@/utils/rpx'; 4import {CircularProgressBase} from 'react-native-circular-progress-indicator'; 5 6import {useSafeAreaInsets} from 'react-native-safe-area-context'; 7import {showPanel} from '../panels/usePanel'; 8import useColors from '@/hooks/useColors'; 9import IconButton from '../base/iconButton'; 10import TrackPlayer from '@/core/trackPlayer'; 11import {musicIsPaused} from '@/utils/trackUtils'; 12import MusicInfo from './musicInfo'; 13import Icon from '@/components/base/icon.tsx'; 14 15function CircularPlayBtn() { 16 const progress = TrackPlayer.useProgress(); 17 const musicState = TrackPlayer.useMusicState(); 18 const colors = useColors(); 19 20 const isPaused = musicIsPaused(musicState); 21 22 return ( 23 <CircularProgressBase 24 activeStrokeWidth={rpx(4)} 25 inActiveStrokeWidth={rpx(2)} 26 inActiveStrokeOpacity={0.2} 27 value={ 28 progress?.duration 29 ? (100 * progress.position) / progress.duration 30 : 0 31 } 32 duration={100} 33 radius={rpx(36)} 34 activeStrokeColor={colors.musicBarText} 35 inActiveStrokeColor={colors.textSecondary}> 36 <IconButton 37 accessibilityLabel={'播放或暂停歌曲'} 38 name={isPaused ? 'play' : 'pause'} 39 sizeType={'normal'} 40 hitSlop={{ 41 top: 10, 42 left: 10, 43 right: 10, 44 bottom: 10, 45 }} 46 color={colors.musicBarText} 47 onPress={async () => { 48 if (isPaused) { 49 await TrackPlayer.play(); 50 } else { 51 await TrackPlayer.pause(); 52 } 53 }} 54 /> 55 </CircularProgressBase> 56 ); 57} 58function MusicBar() { 59 const musicItem = TrackPlayer.useCurrentMusic(); 60 61 const [showKeyboard, setKeyboardStatus] = useState(false); 62 63 const colors = useColors(); 64 const safeAreaInsets = useSafeAreaInsets(); 65 66 useEffect(() => { 67 const showSubscription = Keyboard.addListener('keyboardDidShow', () => { 68 setKeyboardStatus(true); 69 }); 70 const hideSubscription = Keyboard.addListener('keyboardDidHide', () => { 71 setKeyboardStatus(false); 72 }); 73 74 return () => { 75 showSubscription.remove(); 76 hideSubscription.remove(); 77 }; 78 }, []); 79 80 return ( 81 <> 82 {musicItem && !showKeyboard && ( 83 <View 84 style={[ 85 style.wrapper, 86 { 87 backgroundColor: colors.musicBar, 88 paddingRight: safeAreaInsets.right + rpx(24), 89 }, 90 ]} 91 accessible 92 accessibilityLabel={`歌曲: ${musicItem.title} 歌手: ${musicItem.artist}`} 93 // onPress={() => { 94 // navigate(ROUTE_PATH.MUSIC_DETAIL); 95 // }} 96 > 97 <MusicInfo musicItem={musicItem} /> 98 <View style={style.actionGroup}> 99 <CircularPlayBtn /> 100 <Icon 101 accessible 102 accessibilityLabel="播放列表" 103 name="playlist" 104 size={rpx(56)} 105 onPress={() => { 106 showPanel('PlayList'); 107 }} 108 color={colors.musicBarText} 109 style={[style.actionIcon]} 110 /> 111 </View> 112 </View> 113 )} 114 </> 115 ); 116} 117 118export default memo(MusicBar, () => true); 119 120const style = StyleSheet.create({ 121 wrapper: { 122 width: '100%', 123 height: rpx(132), 124 flexDirection: 'row', 125 alignItems: 'center', 126 paddingRight: rpx(24), 127 }, 128 actionGroup: { 129 width: rpx(200), 130 justifyContent: 'flex-end', 131 flexDirection: 'row', 132 alignItems: 'center', 133 }, 134 actionIcon: { 135 marginLeft: rpx(36), 136 }, 137}); 138