xref: /MusicFree/src/components/musicBar/index.tsx (revision 15900d057ad4df766b2f9ea5b48f92a8ce2664db)
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