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.text} 40 inActiveStrokeColor={Color(colors.text).alpha(0.5).toString()}> 41 <IconButton 42 accessibilityLabel={isPaused ? '播放' : '暂停'} 43 name={isPaused ? 'play' : 'pause'} 44 sizeType={'normal'} 45 onPress={async () => { 46 if (isPaused) { 47 await MusicQueue.play(); 48 } else { 49 await MusicQueue.pause(); 50 } 51 }} 52 /> 53 </CircularProgressBase> 54 ); 55} 56function MusicBar() { 57 const musicItem = MusicQueue.useCurrentMusicItem(); 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"> 109 {musicItem?.title} 110 </ThemeText> 111 {musicItem?.artist && ( 112 <ThemeText 113 fontSize="description" 114 fontColor="secondary"> 115 {' '} 116 -{musicItem.artist} 117 </ThemeText> 118 )} 119 </Text> 120 <View style={style.actionGroup}> 121 <CircularPlayBtn /> 122 <Icon 123 accessible 124 accessibilityLabel="播放列表" 125 name="playlist-music" 126 size={rpx(56)} 127 onPress={() => { 128 showPanel('PlayList'); 129 }} 130 style={[style.actionIcon, {color: colors.text}]} 131 /> 132 </View> 133 </Pressable> 134 )} 135 </> 136 ); 137} 138 139export default memo(MusicBar, () => true); 140 141const style = StyleSheet.create({ 142 wrapper: { 143 width: '100%', 144 height: rpx(132), 145 flexDirection: 'row', 146 alignItems: 'center', 147 paddingHorizontal: rpx(24), 148 }, 149 artworkWrapper: { 150 height: rpx(120), 151 width: rpx(120), 152 justifyContent: 'center', 153 }, 154 textWrapper: { 155 flexGrow: 1, 156 flexShrink: 1, 157 }, 158 actionGroup: { 159 width: rpx(200), 160 justifyContent: 'flex-end', 161 flexDirection: 'row', 162 alignItems: 'center', 163 }, 164 actionIcon: { 165 marginLeft: rpx(36), 166 }, 167 artworkImg: { 168 width: rpx(96), 169 height: rpx(96), 170 borderRadius: rpx(48), 171 }, 172}); 173