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