1import React, {memo, useEffect, useState} from 'react'; 2import {Keyboard, Pressable, StyleSheet, Text, View} from 'react-native'; 3import rpx from '@/utils/rpx'; 4import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; 5import {CircularProgressBase} from 'react-native-circular-progress-indicator'; 6import {ROUTE_PATH, useNavigate} from '@/entry/router'; 7 8import Color from 'color'; 9import ThemeText from '../base/themeText'; 10import {ImgAsset} from '@/constants/assetsConst'; 11import {useSafeAreaInsets} from 'react-native-safe-area-context'; 12import {showPanel} from '../panels/usePanel'; 13import FastImage from '../base/fastImage'; 14import useColors from '@/hooks/useColors'; 15import IconButton from '../base/iconButton'; 16import TrackPlayer from '@/core/trackPlayer'; 17import {musicIsPaused} from '@/utils/trackUtils'; 18 19function CircularPlayBtn() { 20 const progress = TrackPlayer.useProgress(); 21 const musicState = TrackPlayer.useMusicState(); 22 const colors = useColors(); 23 24 const isPaused = musicIsPaused(musicState); 25 26 return ( 27 <CircularProgressBase 28 activeStrokeWidth={rpx(4)} 29 inActiveStrokeWidth={rpx(2)} 30 inActiveStrokeOpacity={0.2} 31 value={ 32 progress?.duration 33 ? (100 * progress.position) / progress.duration 34 : 0 35 } 36 duration={100} 37 radius={rpx(36)} 38 activeStrokeColor={colors.musicBarText} 39 inActiveStrokeColor={colors.textSecondary}> 40 <IconButton 41 accessibilityLabel={isPaused ? '播放' : '暂停'} 42 name={isPaused ? 'play' : 'pause'} 43 sizeType={'normal'} 44 color={colors.musicBarText} 45 onPress={async () => { 46 if (isPaused) { 47 await TrackPlayer.play(); 48 } else { 49 await TrackPlayer.pause(); 50 } 51 }} 52 /> 53 </CircularProgressBase> 54 ); 55} 56function MusicBar() { 57 const musicItem = TrackPlayer.useCurrentMusic(); 58 59 const [showKeyboard, setKeyboardStatus] = useState(false); 60 61 const navigate = useNavigate(); 62 const colors = useColors(); 63 const safeAreaInsets = useSafeAreaInsets(); 64 65 useEffect(() => { 66 const showSubscription = Keyboard.addListener('keyboardDidShow', () => { 67 setKeyboardStatus(true); 68 }); 69 const hideSubscription = Keyboard.addListener('keyboardDidHide', () => { 70 setKeyboardStatus(false); 71 }); 72 73 return () => { 74 showSubscription.remove(); 75 hideSubscription.remove(); 76 }; 77 }, []); 78 79 return ( 80 <> 81 {musicItem && !showKeyboard && ( 82 <Pressable 83 style={[ 84 style.wrapper, 85 { 86 backgroundColor: colors.musicBar, 87 paddingLeft: safeAreaInsets.left + rpx(24), 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 <View style={style.artworkWrapper}> 97 <FastImage 98 style={style.artworkImg} 99 uri={musicItem.artwork} 100 emptySrc={ImgAsset.albumDefault} 101 /> 102 </View> 103 <Text 104 ellipsizeMode="tail" 105 accessible={false} 106 style={style.textWrapper} 107 numberOfLines={1}> 108 <ThemeText fontSize="content" fontColor="musicBarText"> 109 {musicItem?.title} 110 </ThemeText> 111 {musicItem?.artist && ( 112 <ThemeText 113 fontSize="description" 114 color={Color(colors.musicBarText) 115 .alpha(0.6) 116 .toString()}> 117 {' '} 118 -{musicItem.artist} 119 </ThemeText> 120 )} 121 </Text> 122 <View style={style.actionGroup}> 123 <CircularPlayBtn /> 124 <Icon 125 accessible 126 accessibilityLabel="播放列表" 127 name="playlist-music" 128 size={rpx(56)} 129 onPress={() => { 130 showPanel('PlayList'); 131 }} 132 style={[ 133 style.actionIcon, 134 {color: colors.musicBarText}, 135 ]} 136 /> 137 </View> 138 </Pressable> 139 )} 140 </> 141 ); 142} 143 144export default memo(MusicBar, () => true); 145 146const style = StyleSheet.create({ 147 wrapper: { 148 width: '100%', 149 height: rpx(132), 150 flexDirection: 'row', 151 alignItems: 'center', 152 paddingHorizontal: rpx(24), 153 }, 154 artworkWrapper: { 155 height: rpx(120), 156 width: rpx(120), 157 justifyContent: 'center', 158 }, 159 textWrapper: { 160 flexGrow: 1, 161 flexShrink: 1, 162 }, 163 actionGroup: { 164 width: rpx(200), 165 justifyContent: 'flex-end', 166 flexDirection: 'row', 167 alignItems: 'center', 168 }, 169 actionIcon: { 170 marginLeft: rpx(36), 171 }, 172 artworkImg: { 173 width: rpx(96), 174 height: rpx(96), 175 borderRadius: rpx(48), 176 }, 177}); 178