From e1fa63beedd409127fc967047f4d9ff5ea3b3164 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Wed, 7 Jul 2021 11:09:24 +0900 Subject: [PATCH] added the now playing bar --- res/next.png | Bin 0 -> 6090 bytes res/pause-fill.png | Bin 0 -> 2024 bytes res/play-fill.png | Bin 0 -> 3993 bytes src/components/NowPlayingBar.tsx | 140 +++++++++++++++++++++++ src/components/NowPlayingLayout.tsx | 12 +- src/components/TrackPlayerState.tsx | 2 +- src/components/common/BottomTabBar.tsx | 62 +++++----- src/components/common/CoverArt.tsx | 89 +++++++------- src/components/common/PressableImage.tsx | 4 +- src/hooks/trackplayer.ts | 51 --------- src/state/trackplayer.ts | 9 ++ src/styles/colors.ts | 1 - 12 files changed, 241 insertions(+), 129 deletions(-) create mode 100644 res/next.png create mode 100644 res/pause-fill.png create mode 100644 res/play-fill.png create mode 100644 src/components/NowPlayingBar.tsx delete mode 100644 src/hooks/trackplayer.ts diff --git a/res/next.png b/res/next.png new file mode 100644 index 0000000000000000000000000000000000000000..2036056a09a8b25b2aa26278c6db6b36088134eb GIT binary patch literal 6090 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 F2>_^X_+~x z3=A3*YbV-z91aj^^$%XECCYByVUdzCSEr-PB1^O@K=BIKTB{j*zVOd7QPI@vYsx-& zaQ@L%&E1>V@vUo8_`&?>*|Q}tlvM9c7l}GjaadXY`Q74*_jV2$Av4lSdzokD&3>8U zbmm#u(F@v6Usx}6&p4x#>^CXs!`k)Lua@lU{;czVpZ)6iv(Y>p9Y;2wG01(y`{5mDwse2q#68T`Jpz-8+>U{#;NBP36W=!?zt?05?m1Xsm zJF-9YT=bl+UcFn-oy?aw_gedO%yQEv6|s#hoJt~B0^C^59(}p)xpBg>zb*asH@Yh6{QZ)>xnWzVWe%f5QXgf~DL4 zUS%`*>{+#Ut!sZnWA1}TyVhz}A9%`V@FnHMvNv9iQBDgP_RPO%{Iua<^n(1ev!k=+ zm-9QcrA^(%$j8@sQ7Lu(?>oDf7w7%|w&mG(W|rqg)%l;hW-%}@uqAoByD?-RFYVBnqR>EaktaqI2f z`W~6DmB&AxH-5dfIrovgz?^_$>-#%b-~H0Gz)&M|afsg|E#Fy70z)Q;i3ToJbIV+! zp>(J{qi1!~L$53HEHa5qN{U^VB!o>Jq_>*2F=aLcTt8^};Ly8s?;dK%Jo>|Y&OLJT z=P&)YyWCm@n&0Q;?|*-Nf5Q7SoA1|Fm!*}vGck|`)*qEPxH@`o)mP1@ML%sk-%l!; zw#4{u?b4Z|bN{CvIwSS&UqqmYeWuy$->RZMd@Kp`UcG)DJyGSP`q?-BDtn_Z9TNWW z`23FFv6J3;T>diu{oe2Qrq9seIACL7U~u7n-uCTYyBsbpn*1T$?9Si!@f8nSXCFU) zyx%ZUpy6H3!~IL|=H*ZRl0NB_@dp9nN$d2cdR-4+eN{ZNNP!_%>GUT4-M_D^%(dj^ z|2}1j_Gg>@kwLSUvNIXDUt1d;ZddZ+Lbz&e6`x$){v``-&j0Uy%FbkPnvI=(x}0^{ z8|$=h4=wLav2_jPF`a$3R>xVOA#y|6+griWYp=z5J}*6~COT=~+jsF_YzcPFWwoEEkA8_#K>%}Yg2_vuA#d6AvAZDaTR_R@F%WYXw7%&e?_*?3FJF#eI(-}mE@dex7k`kvl*Ygb&&vK4f?v8C?sFIJs&frja^0w;?r zmMxJ@PENMxQ>>Yy%6pH=!;|A-$C6;>8bA3dTP)9i{vCO*!-%1_wswD{)>Jp8`n>l> zo4a3s{WV#q*yXF(f)K5>k5^uGzf-HyQczo8|6j6)*>EniM`f0(@Bc#=pLZ=7=q{nZF$B>;G3vuroTeoOx5;wWva&WrOwUO`B(OFgnaJ z+wuFKz@fEMy`JXwG&3li+ggw~N#&}}>9)-}(Z@VE7#?lDaku@>-f7ETUN~^zfRdXN zgTVaDXL5Gj&FgOyuv1+pA;93WQt73%VoST$)KkAfw#+%_KPe@jBe9(0q0BKy27%?g zlfJbIlr77gWyHtSU~&Ddo3ZrCO*t06m!I~uFeun{->p4yCM$Cm-_p!qYG4bdrkUlW zEmmytU8Bv^VDZ)SmY`F`j{W=p{{=bG?{(Lr9o8)ytbH#ROR|H!Fym%@n?PBZ*3<(Q z91M?QlBFlBR4TO;zB8?dvHNU~s8$P{>SP z8B%4_vY}aemH@+%%!NyQT?4Od()qpm>gw?Mhrl6rzIOkeFrgrM|SyCqAE!&^` z;jqA=YH+yR(|x&5;LzO2X@9LAD=>6i1}C-+%D$JkgW}=G6xCd#%~{h@4>L0}=c_R| z)J)s)J7D+SbxSj?Oe`#ROkiO6XnFTc+wx`0@8Za>&NnabcFX_W>7KVp|uk7;NYOnS1t3 z+wHgCGB_RvznTaN8w*2G&-)yTHdB_ptYTs~^0LNww|dJ4>y`pOMutaQ953w?ICRd$ zHyCXAmWyZHHt9(FT`sX?ge1>1Irr``&1GOvzklkS?ge*ia}!Sv27&o@zV|OYdHC<& zzwaP7f>X`uP4zFgy_XPRNSLrlcKKYRa45GtdnQkX!C{Y`RrGv81qO%mU)~_4ckf@nfB(N66T=bdMt@-j zg?kJ&HOum2ycinZZLKLP`ozq_&@pA^-subsAK4zvWnt*Jz7s^Onz=__k&$6n%6jkzY8)b__a^hi7eu0WO(%D?!<``#TXfsLPU<9HU2BX!tlbc+kb7U}1Q{Xa2Nu zU+y;fDGUt7?~i#c{d99@#RJweax;u`%lGfNm{Fs8zF3e!LFipv@%Qs4v(HvPE3&TO zW;nBMx=L;8$w{iu&wcHlugs7jHuL6pRo<(wzcSz5~ahk8Sd?H zy7YFE-pcE*&;H!E;jD}Q@^S{6H z6JmJq|8{bkc==_JYJ<;mlfZRA(#=FB29L#6KfnL5Stk4ZJA=UH^t*q#r+Ph=t(^8{9M_$dC89QI)Wa!nbuRGlq3%*t0U`Q&8xupAUpXvF}%RyP$=H06~H|r+_3NRdb z?C3lH=KlF$CZ}(G+J7^*kFR!cHOxy&v!7fNxHQOewz$1JL&xzYGjsK)pT6zp?mmD1 zYUx}7hJ-TN=_TDuPQU*8%K}s$G<)Bgs&X=QYt-6P790#ol}~s7cV8KDD`xf8x&tO0 z439qUShBX)t$A`a3xlAg#>+07^ZC=Rq{*-_6wWZ&ygzqqRDJP>2M5JLCB975qK88}l(WRJ?il=ifa$H%A76)Am2#DSJ+eiCTaCeUcOl!^aA*OH+H@ znvH*hY5tgG_PuzQp@(U+KO(R0dl9uBo*haQMvky6&N~>&sdUMIq&`z%P;JV4lJPLvA67X4im$lqTsZ$*g%oP zb8f0LbhsN!v`MdSf4*sk>73>Dz;}vKh<2iQ2w?7r!Dz>-~#6R^Ujp?PU0~C^@aH zc9KeX=<2J_rFz+f9sSuGMD5=0SRy@HQyfG}_Z*u;#b>y`lp{llf^8ZVm zmnVsRf1&3$>*lwjoqKXiO1@N1Gm7gETL13Gks~hI zKR-SFoAxc*c3ZZt=k3!^KdG;}`bt(ZplSroZ8 zZ1tv+gMtULEsCC;xG1U}_Qf;G{QUjr_mk7apH$YpeR#N?e`C%;!2|0v&dxGjE^lA= zM}1P**~4e+V=i4gcf5>|>4VjMt{R_9-=AGnlb=%JI`8=%E9pI~7M%I4ZsBVpK4yP@ zu=app`Tbv)y!B%(%*?(`__2vEEy83!`-Y;Yr_Re-mAugL+<(7y_Vr0$zN&a``!KWi zgIGcv4EAB;dKtd|b8)fz>hw8<$JV~v=X1%@*rcD+bNY0zrQEN-{yO~VjGYD3i&Kjm z6Vjs3yy;h+TO{=FRLrIOzP`S9jqL2|dN`XI&M-$#(=GmPZ?xII^!xp3UzQ2~c++d*E3Uu3|2#mRB|%PFMkdC>(sJjyGv}sH@v2%qbMMm6^OxWGJ7eb6vpGlZ%AfxH mr(v1?TTu6zl-|t(_dom`_T}q5F1|kx(&y>w=d#Wzp$Py=X+os{ literal 0 HcmV?d00001 diff --git a/src/components/NowPlayingBar.tsx b/src/components/NowPlayingBar.tsx new file mode 100644 index 0000000..9d04826 --- /dev/null +++ b/src/components/NowPlayingBar.tsx @@ -0,0 +1,140 @@ +import React from 'react' +import { Pressable, StyleSheet, Text, View } from 'react-native' +import { useNavigation } from '@react-navigation/native' +import { useAtomValue } from 'jotai/utils' +import { currentTrackAtom, playerStateAtom, usePause, usePlay, useProgress } from '../state/trackplayer' +import CoverArt from './common/CoverArt' +import colors from '../styles/colors' +import { Font } from '../styles/text' +import PressableImage from './common/PressableImage' +import { State } from 'react-native-track-player' + +const ProgressBar = () => { + const { position, duration } = useProgress() + + let progress = 0 + if (duration > 0) { + progress = position / duration + } + + return ( + + + + + ) +} + +const progressStyles = StyleSheet.create({ + container: { + height: 1, + flexDirection: 'row', + }, + left: { + backgroundColor: colors.text.primary, + }, + right: { + backgroundColor: '#595959', + }, +}) + +const NowPlayingBar = () => { + const navigation = useNavigation() + const track = useAtomValue(currentTrackAtom) + const playerState = useAtomValue(playerStateAtom) + const play = usePlay() + const pause = usePause() + + let playPauseIcon: number + let playPauseAction: () => void + + switch (playerState) { + case State.Playing: + case State.Buffering: + case State.Connecting: + playPauseIcon = require('../../res/pause-fill.png') + playPauseAction = pause + break + default: + playPauseIcon = require('../../res/play-fill.png') + playPauseAction = play + break + } + + return ( + navigation.navigate('Now Playing')} + style={{ ...styles.container, display: track ? 'flex' : 'none' }}> + + + hi} + height={styles.subContainer.height} + width={styles.subContainer.height} + coverArtUri={track?.artwork as string} + /> + + + {track?.title} + + + {track?.artist} + + + + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + width: '100%', + backgroundColor: colors.gradient.high, + borderBottomColor: colors.gradient.low, + borderBottomWidth: 1, + }, + subContainer: { + height: 60, + flexDirection: 'row', + }, + detailsContainer: { + flex: 1, + height: '100%', + justifyContent: 'center', + marginLeft: 10, + // backgroundColor: 'green', + }, + detailsTitle: { + fontFamily: Font.semiBold, + fontSize: 13, + color: colors.text.primary, + }, + detailsAlbum: { + fontFamily: Font.regular, + fontSize: 13, + color: colors.text.secondary, + }, + controls: { + flexDirection: 'row', + height: '100%', + justifyContent: 'center', + alignItems: 'center', + marginRight: 18, + marginLeft: 12, + }, + play: { + height: 32, + width: 32, + }, +}) + +export default NowPlayingBar diff --git a/src/components/NowPlayingLayout.tsx b/src/components/NowPlayingLayout.tsx index 121a710..9b45139 100644 --- a/src/components/NowPlayingLayout.tsx +++ b/src/components/NowPlayingLayout.tsx @@ -2,12 +2,14 @@ import { useAtomValue } from 'jotai/utils' import React from 'react' import { StatusBar, StyleSheet, Text, useWindowDimensions, View } from 'react-native' import FastImage from 'react-native-fast-image' -import TrackPlayer, { State } from 'react-native-track-player' +import { State } from 'react-native-track-player' import { - queueNameAtom, currentTrackAtom, playerStateAtom, + queueNameAtom, useNext, + usePause, + usePlay, usePrevious, useProgress, } from '../state/trackplayer' @@ -171,6 +173,8 @@ const seekStyles = StyleSheet.create({ const PlayerControls = () => { const state = useAtomValue(playerStateAtom) + const play = usePlay() + const pause = usePause() const next = useNext() const previous = usePrevious() @@ -184,12 +188,12 @@ const PlayerControls = () => { case State.Connecting: disabled = false playPauseIcon = require('../../res/pause_circle-fill.png') - playPauseAction = () => TrackPlayer.pause() + playPauseAction = pause break case State.Paused: disabled = false playPauseIcon = require('../../res/play_circle-fill.png') - playPauseAction = () => TrackPlayer.play() + playPauseAction = play break default: disabled = true diff --git a/src/components/TrackPlayerState.tsx b/src/components/TrackPlayerState.tsx index 630fdfc..fc5b725 100644 --- a/src/components/TrackPlayerState.tsx +++ b/src/components/TrackPlayerState.tsx @@ -127,7 +127,7 @@ const ProgressState = () => { return ( <> - + ) } diff --git a/src/components/common/BottomTabBar.tsx b/src/components/common/BottomTabBar.tsx index e187ea6..edf89b8 100644 --- a/src/components/common/BottomTabBar.tsx +++ b/src/components/common/BottomTabBar.tsx @@ -4,6 +4,7 @@ import { BottomTabBarProps } from '@react-navigation/bottom-tabs' import textStyles from '../../styles/text' import colors from '../../styles/colors' import FastImage from 'react-native-fast-image' +import NowPlayingBar from '../NowPlayingBar' const icons: { [key: string]: any } = { home: { @@ -77,36 +78,39 @@ const BottomTabButton: React.FC<{ const BottomTabBar: React.FC = ({ state, descriptors, navigation }) => { return ( - - {state.routes.map((route, index) => { - const { options } = descriptors[route.key] as any - const label = - options.tabBarLabel !== undefined - ? (options.tabBarLabel as string) - : options.title !== undefined - ? options.title - : route.name + + + + {state.routes.map((route, index) => { + const { options } = descriptors[route.key] as any + const label = + options.tabBarLabel !== undefined + ? (options.tabBarLabel as string) + : options.title !== undefined + ? options.title + : route.name - return ( - - ) - })} + return ( + + ) + })} + ) } diff --git a/src/components/common/CoverArt.tsx b/src/components/common/CoverArt.tsx index acbe84b..f1757db 100644 --- a/src/components/common/CoverArt.tsx +++ b/src/components/common/CoverArt.tsx @@ -1,58 +1,63 @@ -import React, { useState } from 'react' -import { ActivityIndicator, View } from 'react-native' +import React, { useEffect, useState } from 'react' +import { ActivityIndicator, StyleSheet, View } from 'react-native' import FastImage from 'react-native-fast-image' import colors from '../../styles/colors' const CoverArt: React.FC<{ PlaceholderComponent: () => JSX.Element - height: number - width: number + height?: string | number + width?: string | number coverArtUri?: string }> = ({ PlaceholderComponent, height, width, coverArtUri }) => { const [placeholderVisible, setPlaceholderVisible] = useState(false) const [loading, setLoading] = useState(true) - const indicatorSize = height > 130 ? 'large' : 'small' - const halfIndicatorHeight = indicatorSize === 'large' ? 18 : 10 + useEffect(() => { + if (!coverArtUri) { + setLoading(false) + } + }, [coverArtUri, setLoading]) - const Placeholder: React.FC<{ visible: boolean }> = ({ visible }) => ( - - + const Image = () => ( + { + setLoading(false) + setPlaceholderVisible(true) + }} + onLoadEnd={() => setLoading(false)} + /> + ) + + return ( + + {coverArtUri ? : <>} + + + + ) - - const Art = () => ( - <> - - - { - setLoading(false) - setPlaceholderVisible(true) - }} - onLoadEnd={() => setLoading(false)} - /> - - ) - - return {!coverArtUri ? : } } +const styles = StyleSheet.create({ + container: {}, + image: { + height: '100%', + width: '100%', + }, + placeholderContainer: { + height: '100%', + width: '100%', + position: 'absolute', + }, + indicator: { + height: '100%', + width: '100%', + position: 'absolute', + }, +}) + export default React.memo(CoverArt) diff --git a/src/components/common/PressableImage.tsx b/src/components/common/PressableImage.tsx index e32bfc5..d347f1b 100644 --- a/src/components/common/PressableImage.tsx +++ b/src/components/common/PressableImage.tsx @@ -8,7 +8,8 @@ const PressableImage: React.FC<{ style?: ViewStyle tintColor?: string disabled?: boolean -}> = ({ source, onPress, style, tintColor, disabled }) => { + hitSlop?: number +}> = ({ source, onPress, style, tintColor, disabled, hitSlop }) => { const [opacity, setOpacity] = useState(1) const [dimensions, setDimensions] = useState(undefined) @@ -27,6 +28,7 @@ const PressableImage: React.FC<{ style={style} onPress={onPress} disabled={disabled} + hitSlop={hitSlop} onPressIn={() => { if (!disabled) { setOpacity(0.4) diff --git a/src/hooks/trackplayer.ts b/src/hooks/trackplayer.ts deleted file mode 100644 index 74d7d72..0000000 --- a/src/hooks/trackplayer.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { useUpdateAtom } from 'jotai/utils' -import TrackPlayer, { Track } from 'react-native-track-player' -import { Song } from '../models/music' -import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer' - -function mapSongToTrack(song: Song, queueName: string): Track { - return { - id: song.id, - queueName, - title: song.title, - artist: song.artist || 'Unknown Artist', - url: song.streamUri, - artwork: song.coverArtUri, - artworkThumb: song.coverArtThumbUri, - duration: song.duration, - } -} - -export const useSetQueue = () => { - const setCurrentTrack = useUpdateAtom(currentTrackAtom) - const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom) - - return async (songs: Song[], name: string, playId?: string) => { - await TrackPlayer.reset() - const tracks = songs.map(s => mapSongToTrack(s, name)) - - setCurrentQueueName(name) - if (playId) { - setCurrentTrack(tracks.find(t => t.id === playId)) - } - - if (!playId) { - await TrackPlayer.add(tracks) - } else if (playId === tracks[0].id) { - await TrackPlayer.add(tracks) - await TrackPlayer.play() - } else { - const playIndex = tracks.findIndex(t => t.id === playId) - const tracks1 = tracks.slice(0, playIndex) - const tracks2 = tracks.slice(playIndex) - - await TrackPlayer.add(tracks2) - await TrackPlayer.play() - - await TrackPlayer.add(tracks1, 0) - - // const queue = await TrackPlayer.getQueue(); - // console.log(`queue: ${JSON.stringify(queue.map(x => x.title))}`); - } - } -} diff --git a/src/state/trackplayer.ts b/src/state/trackplayer.ts index 9dcdcaf..5459846 100644 --- a/src/state/trackplayer.ts +++ b/src/state/trackplayer.ts @@ -145,6 +145,14 @@ export const useRefreshProgress = () => { }) } +export const usePlay = () => { + return () => trackPlayerCommands.enqueue(() => TrackPlayer.play()) +} + +export const usePause = () => { + return () => trackPlayerCommands.enqueue(() => TrackPlayer.pause()) +} + export const usePrevious = () => { const setCurrentTrack = useUpdateAtom(currentTrackAtom) @@ -259,6 +267,7 @@ function mapSongToTrack(song: Song, queueName: string): TrackExt { queueName, title: song.title, artist: song.artist || 'Unknown Artist', + album: song.album || 'Unknown Album', url: song.streamUri, artwork: song.coverArtUri, artworkThumb: song.coverArtThumbUri, diff --git a/src/styles/colors.ts b/src/styles/colors.ts index 50bd00d..6de496d 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -5,7 +5,6 @@ export default { }, gradient: { high: '#2d2d2d', - mid: '#191919', low: '#000000', }, accent: '#b134db',