mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
added seeking to now playing/notification
This commit is contained in:
parent
9705a95aaa
commit
2d2efe3dcf
@ -21,8 +21,8 @@ const ProgressBar = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={progressStyles.container}>
|
<View style={progressStyles.container}>
|
||||||
<View style={{ ...progressStyles.left, flex: progress }} />
|
<View style={[progressStyles.left, { flex: progress }]} />
|
||||||
<View style={{ ...progressStyles.right, flex: 1 - progress }} />
|
<View style={[progressStyles.right, { flex: 1 - progress }]} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -52,8 +52,6 @@ const NowPlayingBar = () => {
|
|||||||
|
|
||||||
switch (playerState) {
|
switch (playerState) {
|
||||||
case State.Playing:
|
case State.Playing:
|
||||||
case State.Buffering:
|
|
||||||
case State.Connecting:
|
|
||||||
playPauseIcon = 'pause'
|
playPauseIcon = 'pause'
|
||||||
playPauseAction = pause
|
playPauseAction = pause
|
||||||
break
|
break
|
||||||
|
|||||||
@ -60,6 +60,13 @@ export const useSkipTo = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useSeekTo = () => {
|
||||||
|
return (position: number) =>
|
||||||
|
trackPlayerCommands.enqueue(async () => {
|
||||||
|
await TrackPlayer.seekTo(position)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const useToggleRepeat = () => {
|
export const useToggleRepeat = () => {
|
||||||
const setRepeatMode = useStore(selectTrackPlayer.setRepeatMode)
|
const setRepeatMode = useStore(selectTrackPlayer.setRepeatMode)
|
||||||
|
|
||||||
|
|||||||
@ -82,6 +82,12 @@ const createService = async () => {
|
|||||||
TrackPlayer.addEventListener(Event.PlaybackMetadataReceived, () => {
|
TrackPlayer.addEventListener(Event.PlaybackMetadataReceived, () => {
|
||||||
setCurrentTrackIdx(useStore.getState().currentTrackIdx)
|
setCurrentTrackIdx(useStore.getState().currentTrackIdx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
TrackPlayer.addEventListener(Event.RemoteSeek, data => {
|
||||||
|
trackPlayerCommands.enqueue(async () => {
|
||||||
|
await TrackPlayer.seekTo(data.position)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = async function () {
|
module.exports = async function () {
|
||||||
|
|||||||
@ -3,7 +3,15 @@ import ImageGradientBackground from '@app/components/ImageGradientBackground'
|
|||||||
import PressableOpacity from '@app/components/PressableOpacity'
|
import PressableOpacity from '@app/components/PressableOpacity'
|
||||||
import Star from '@app/components/Star'
|
import Star from '@app/components/Star'
|
||||||
import { useStarred } from '@app/hooks/music'
|
import { useStarred } from '@app/hooks/music'
|
||||||
import { useNext, usePause, usePlay, usePrevious, useToggleRepeat, useToggleShuffle } from '@app/hooks/trackplayer'
|
import {
|
||||||
|
useNext,
|
||||||
|
usePause,
|
||||||
|
usePlay,
|
||||||
|
usePrevious,
|
||||||
|
useSeekTo,
|
||||||
|
useToggleRepeat,
|
||||||
|
useToggleShuffle,
|
||||||
|
} from '@app/hooks/trackplayer'
|
||||||
import { selectMusic } from '@app/state/music'
|
import { selectMusic } from '@app/state/music'
|
||||||
import { useStore } from '@app/state/store'
|
import { useStore } from '@app/state/store'
|
||||||
import { QueueContextType, selectTrackPlayer } from '@app/state/trackplayer'
|
import { QueueContextType, selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
@ -12,7 +20,7 @@ import dimensions from '@app/styles/dimensions'
|
|||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import formatDuration from '@app/util/formatDuration'
|
import formatDuration from '@app/util/formatDuration'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { StatusBar, StyleSheet, Text, View } from 'react-native'
|
import { StatusBar, StyleSheet, Text, View } from 'react-native'
|
||||||
import { NativeStackScreenProps } from 'react-native-screens/native-stack'
|
import { NativeStackScreenProps } from 'react-native-screens/native-stack'
|
||||||
import { RepeatMode, State } from 'react-native-track-player'
|
import { RepeatMode, State } from 'react-native-track-player'
|
||||||
@ -21,6 +29,7 @@ import IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
|||||||
import Icon from 'react-native-vector-icons/Ionicons'
|
import Icon from 'react-native-vector-icons/Ionicons'
|
||||||
import IconMatCom from 'react-native-vector-icons/MaterialCommunityIcons'
|
import IconMatCom from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||||
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
||||||
|
import Slider from '@react-native-community/slider'
|
||||||
|
|
||||||
function getContextName(type?: QueueContextType) {
|
function getContextName(type?: QueueContextType) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -131,7 +140,8 @@ const coverArtStyles = StyleSheet.create({
|
|||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingBottom: 20,
|
paddingBottom: 10,
|
||||||
|
paddingHorizontal: 10,
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@ -169,6 +179,7 @@ const infoStyles = StyleSheet.create({
|
|||||||
container: {
|
container: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
paddingHorizontal: 10,
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -193,21 +204,46 @@ const infoStyles = StyleSheet.create({
|
|||||||
|
|
||||||
const SeekBar = () => {
|
const SeekBar = () => {
|
||||||
const { position, duration } = useStore(selectTrackPlayer.progress)
|
const { position, duration } = useStore(selectTrackPlayer.progress)
|
||||||
|
const seekTo = useSeekTo()
|
||||||
|
const [value, setValue] = useState(0)
|
||||||
|
const [sliding, setSliding] = useState(false)
|
||||||
|
|
||||||
let progress = 0
|
useEffect(() => {
|
||||||
if (duration > 0) {
|
if (sliding) {
|
||||||
progress = position / duration
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setValue(position)
|
||||||
|
}, [position, sliding])
|
||||||
|
|
||||||
|
const onSlidingStart = useCallback(() => {
|
||||||
|
setSliding(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onSlidingComplete = useCallback(
|
||||||
|
async (val: number) => {
|
||||||
|
await seekTo(val)
|
||||||
|
setSliding(false)
|
||||||
|
},
|
||||||
|
[seekTo],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={seekStyles.container}>
|
<View style={seekStyles.container}>
|
||||||
<View style={seekStyles.barContainer}>
|
<View style={seekStyles.barContainer}>
|
||||||
<View style={{ ...seekStyles.bars, ...seekStyles.barLeft, flex: progress }} />
|
<Slider
|
||||||
<View style={{ ...seekStyles.indicator }} />
|
style={seekStyles.slider}
|
||||||
<View style={{ ...seekStyles.bars, ...seekStyles.barRight, flex: 1 - progress }} />
|
minimumTrackTintColor="white"
|
||||||
|
maximumTrackTintColor={colors.text.secondary}
|
||||||
|
thumbTintColor="white"
|
||||||
|
maximumValue={duration}
|
||||||
|
value={value}
|
||||||
|
onSlidingStart={onSlidingStart}
|
||||||
|
onSlidingComplete={onSlidingComplete}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={seekStyles.textContainer}>
|
<View style={seekStyles.textContainer}>
|
||||||
<Text style={seekStyles.text}>{formatDuration(position)}</Text>
|
<Text style={seekStyles.text}>{formatDuration(value)}</Text>
|
||||||
<Text style={seekStyles.text}>{formatDuration(duration)}</Text>
|
<Text style={seekStyles.text}>{formatDuration(duration)}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -217,17 +253,21 @@ const SeekBar = () => {
|
|||||||
const seekStyles = StyleSheet.create({
|
const seekStyles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
marginTop: 26,
|
marginTop: 16,
|
||||||
},
|
},
|
||||||
barContainer: {
|
barContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 6,
|
marginBottom: 0,
|
||||||
},
|
},
|
||||||
bars: {
|
bars: {
|
||||||
backgroundColor: colors.text.primary,
|
backgroundColor: colors.text.primary,
|
||||||
height: 4,
|
height: 4,
|
||||||
},
|
},
|
||||||
|
slider: {
|
||||||
|
flex: 1,
|
||||||
|
height: 40,
|
||||||
|
},
|
||||||
barLeft: {
|
barLeft: {
|
||||||
marginRight: -6,
|
marginRight: -6,
|
||||||
},
|
},
|
||||||
@ -245,6 +285,7 @@ const seekStyles = StyleSheet.create({
|
|||||||
textContainer: {
|
textContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
|
paddingHorizontal: 10,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontFamily: font.regular,
|
fontFamily: font.regular,
|
||||||
@ -271,8 +312,6 @@ const PlayerControls = () => {
|
|||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case State.Playing:
|
case State.Playing:
|
||||||
case State.Buffering:
|
|
||||||
case State.Connecting:
|
|
||||||
disabled = false
|
disabled = false
|
||||||
playPauseIcon = 'pause-circle'
|
playPauseIcon = 'pause-circle'
|
||||||
playPauseAction = pause
|
playPauseAction = pause
|
||||||
@ -327,6 +366,7 @@ const PlayerControls = () => {
|
|||||||
const controlsStyles = StyleSheet.create({
|
const controlsStyles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
paddingHorizontal: 10,
|
||||||
},
|
},
|
||||||
top: {
|
top: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@ -395,7 +435,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingHorizontal: 30,
|
paddingHorizontal: 20,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
1
index.js
1
index.js
@ -24,6 +24,7 @@ async function start() {
|
|||||||
Capability.Stop,
|
Capability.Stop,
|
||||||
Capability.SkipToNext,
|
Capability.SkipToNext,
|
||||||
Capability.SkipToPrevious,
|
Capability.SkipToPrevious,
|
||||||
|
Capability.SeekTo,
|
||||||
],
|
],
|
||||||
compactCapabilities: [
|
compactCapabilities: [
|
||||||
Capability.Play, //
|
Capability.Play, //
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
"@react-native-async-storage/async-storage": "^1.15.5",
|
"@react-native-async-storage/async-storage": "^1.15.5",
|
||||||
"@react-native-community/hooks": "^2.6.0",
|
"@react-native-community/hooks": "^2.6.0",
|
||||||
"@react-native-community/masked-view": "^0.1.11",
|
"@react-native-community/masked-view": "^0.1.11",
|
||||||
|
"@react-native-community/slider": "^3.0.3",
|
||||||
"@react-navigation/bottom-tabs": "^5.11.11",
|
"@react-navigation/bottom-tabs": "^5.11.11",
|
||||||
"@react-navigation/material-top-tabs": "^5.3.15",
|
"@react-navigation/material-top-tabs": "^5.3.15",
|
||||||
"@react-navigation/native": "^5.9.4",
|
"@react-navigation/native": "^5.9.4",
|
||||||
|
|||||||
@ -1118,6 +1118,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
|
resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
|
||||||
integrity sha512-rQfMIGSR/1r/SyN87+VD8xHHzDYeHaJq6elOSCAD+0iLagXkSI2pfA0LmSXP21uw5i3em7GkkRjfJ8wpqWXZNw==
|
integrity sha512-rQfMIGSR/1r/SyN87+VD8xHHzDYeHaJq6elOSCAD+0iLagXkSI2pfA0LmSXP21uw5i3em7GkkRjfJ8wpqWXZNw==
|
||||||
|
|
||||||
|
"@react-native-community/slider@^3.0.3":
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-native-community/slider/-/slider-3.0.3.tgz#830167fd757ba70ac638747ba3169b2dbae60330"
|
||||||
|
integrity sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw==
|
||||||
|
|
||||||
"@react-native/assets@1.0.0":
|
"@react-native/assets@1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
|
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user