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