diff --git a/app/components/NowPlayingBar.tsx b/app/components/NowPlayingBar.tsx
index 8c165bd..4f2fa60 100644
--- a/app/components/NowPlayingBar.tsx
+++ b/app/components/NowPlayingBar.tsx
@@ -21,8 +21,8 @@ const ProgressBar = () => {
return (
-
-
+
+
)
}
@@ -52,8 +52,6 @@ const NowPlayingBar = () => {
switch (playerState) {
case State.Playing:
- case State.Buffering:
- case State.Connecting:
playPauseIcon = 'pause'
playPauseAction = pause
break
diff --git a/app/hooks/trackplayer.ts b/app/hooks/trackplayer.ts
index face3fe..63a3ce0 100644
--- a/app/hooks/trackplayer.ts
+++ b/app/hooks/trackplayer.ts
@@ -60,6 +60,13 @@ export const useSkipTo = () => {
})
}
+export const useSeekTo = () => {
+ return (position: number) =>
+ trackPlayerCommands.enqueue(async () => {
+ await TrackPlayer.seekTo(position)
+ })
+}
+
export const useToggleRepeat = () => {
const setRepeatMode = useStore(selectTrackPlayer.setRepeatMode)
diff --git a/app/playbackservice.ts b/app/playbackservice.ts
index a4c9b7a..53d9cea 100644
--- a/app/playbackservice.ts
+++ b/app/playbackservice.ts
@@ -82,6 +82,12 @@ const createService = async () => {
TrackPlayer.addEventListener(Event.PlaybackMetadataReceived, () => {
setCurrentTrackIdx(useStore.getState().currentTrackIdx)
})
+
+ TrackPlayer.addEventListener(Event.RemoteSeek, data => {
+ trackPlayerCommands.enqueue(async () => {
+ await TrackPlayer.seekTo(data.position)
+ })
+ })
}
module.exports = async function () {
diff --git a/app/screens/NowPlayingView.tsx b/app/screens/NowPlayingView.tsx
index 1c311b3..3d76a2e 100644
--- a/app/screens/NowPlayingView.tsx
+++ b/app/screens/NowPlayingView.tsx
@@ -3,7 +3,15 @@ import ImageGradientBackground from '@app/components/ImageGradientBackground'
import PressableOpacity from '@app/components/PressableOpacity'
import Star from '@app/components/Star'
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 { useStore } from '@app/state/store'
import { QueueContextType, selectTrackPlayer } from '@app/state/trackplayer'
@@ -12,7 +20,7 @@ import dimensions from '@app/styles/dimensions'
import font from '@app/styles/font'
import formatDuration from '@app/util/formatDuration'
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 { NativeStackScreenProps } from 'react-native-screens/native-stack'
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 IconMatCom from 'react-native-vector-icons/MaterialCommunityIcons'
import IconMat from 'react-native-vector-icons/MaterialIcons'
+import Slider from '@react-native-community/slider'
function getContextName(type?: QueueContextType) {
switch (type) {
@@ -131,7 +140,8 @@ const coverArtStyles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
- paddingBottom: 20,
+ paddingBottom: 10,
+ paddingHorizontal: 10,
},
image: {
height: '100%',
@@ -169,6 +179,7 @@ const infoStyles = StyleSheet.create({
container: {
width: '100%',
flexDirection: 'row',
+ paddingHorizontal: 10,
},
details: {
flex: 1,
@@ -193,21 +204,46 @@ const infoStyles = StyleSheet.create({
const SeekBar = () => {
const { position, duration } = useStore(selectTrackPlayer.progress)
+ const seekTo = useSeekTo()
+ const [value, setValue] = useState(0)
+ const [sliding, setSliding] = useState(false)
- let progress = 0
- if (duration > 0) {
- progress = position / duration
- }
+ useEffect(() => {
+ if (sliding) {
+ return
+ }
+
+ setValue(position)
+ }, [position, sliding])
+
+ const onSlidingStart = useCallback(() => {
+ setSliding(true)
+ }, [])
+
+ const onSlidingComplete = useCallback(
+ async (val: number) => {
+ await seekTo(val)
+ setSliding(false)
+ },
+ [seekTo],
+ )
return (
-
-
-
+
- {formatDuration(position)}
+ {formatDuration(value)}
{formatDuration(duration)}
@@ -217,17 +253,21 @@ const SeekBar = () => {
const seekStyles = StyleSheet.create({
container: {
width: '100%',
- marginTop: 26,
+ marginTop: 16,
},
barContainer: {
flexDirection: 'row',
alignItems: 'center',
- marginBottom: 6,
+ marginBottom: 0,
},
bars: {
backgroundColor: colors.text.primary,
height: 4,
},
+ slider: {
+ flex: 1,
+ height: 40,
+ },
barLeft: {
marginRight: -6,
},
@@ -245,6 +285,7 @@ const seekStyles = StyleSheet.create({
textContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
+ paddingHorizontal: 10,
},
text: {
fontFamily: font.regular,
@@ -271,8 +312,6 @@ const PlayerControls = () => {
switch (state) {
case State.Playing:
- case State.Buffering:
- case State.Connecting:
disabled = false
playPauseIcon = 'pause-circle'
playPauseAction = pause
@@ -327,6 +366,7 @@ const PlayerControls = () => {
const controlsStyles = StyleSheet.create({
container: {
width: '100%',
+ paddingHorizontal: 10,
},
top: {
flexDirection: 'row',
@@ -395,7 +435,7 @@ const styles = StyleSheet.create({
},
content: {
flex: 1,
- paddingHorizontal: 30,
+ paddingHorizontal: 20,
},
})
diff --git a/index.js b/index.js
index caa0bf8..e54027b 100644
--- a/index.js
+++ b/index.js
@@ -24,6 +24,7 @@ async function start() {
Capability.Stop,
Capability.SkipToNext,
Capability.SkipToPrevious,
+ Capability.SeekTo,
],
compactCapabilities: [
Capability.Play, //
diff --git a/package.json b/package.json
index 02d01c6..600487e 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"@react-native-async-storage/async-storage": "^1.15.5",
"@react-native-community/hooks": "^2.6.0",
"@react-native-community/masked-view": "^0.1.11",
+ "@react-native-community/slider": "^3.0.3",
"@react-navigation/bottom-tabs": "^5.11.11",
"@react-navigation/material-top-tabs": "^5.3.15",
"@react-navigation/native": "^5.9.4",
diff --git a/yarn.lock b/yarn.lock
index 2ee33cc..ca0a54f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1118,6 +1118,11 @@
resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
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":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"