Skip to content

Commit 65feda3

Browse files
committed
queue feature and player controlls
- add to queue feature - prev, next button - play track with query string - share tracks
1 parent 5abf5fb commit 65feda3

File tree

9 files changed

+193
-41
lines changed

9 files changed

+193
-41
lines changed

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"qs": "^6.5.1",
67
"ramda": "^0.25.0",
78
"react": "^16.2.0",
89
"react-dom": "^16.2.0",
910
"react-redux": "^5.0.6",
1011
"react-router-dom": "^4.2.2",
1112
"react-scripts": "1.1.1",
13+
"react-transition-group": "1.x",
1214
"redux": "^3.7.2",
1315
"redux-logger": "^3.0.6",
14-
"styled-components": "^3.1.6",
15-
"react-transition-group": "1.x"
16+
"styled-components": "^3.1.6"
1617
},
1718
"scripts": {
1819
"start": "react-scripts start",

src/Player/actionTypes.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ export default {
33
PAUSE_TRACK: 'PAUSE_TRACK',
44
RESUME_TRACK: 'RESUME_TRACK',
55
START_PLAYLIST: 'START_PLAYLIST',
6-
ADD_TO_QUEUE: 'ADD_TO_QUEUE'
6+
ADD_TO_QUEUE: 'ADD_TO_QUEUE',
7+
NEXT_TRACK: 'NEXT_TRACK',
8+
PREVIOUS_TRACK: 'PREVIOUS_TRACK'
79
}

src/Player/actions.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import t from './actionTypes'
22

3-
export const startTrack = (surah) => {
3+
export const startTrack = ({surah, suras: playlist}) => {
44
return {
55
type: t.START_TRACK,
6-
payload: surah
6+
payload: {
7+
surah,
8+
playlist
9+
}
710
}
811
}
912

@@ -32,3 +35,15 @@ export const addToQueue = (surah) => {
3235
payload: surah
3336
}
3437
}
38+
39+
export const nextTrack = () => {
40+
return {
41+
type: t.NEXT_TRACK
42+
}
43+
}
44+
45+
export const previousTrack = () => {
46+
return {
47+
type: t.PREVIOUS_TRACK
48+
}
49+
}

src/Player/components/index.js

+34-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React from 'react'
2-
import styled, {css} from 'styled-components'
2+
import styled, { css } from 'styled-components'
33

44
const StyleBase = styled.div`
55
position: fixed;
66
width: 100%;
77
bottom: 0;
8-
color: #FFF;
8+
color: #fff;
99
background-color: #016bcf;
1010
padding: 10px 30px;
1111
> div {
@@ -59,13 +59,13 @@ const Progress = styled.div`
5959
`
6060

6161
const ProgressStatus = styled.div.attrs({
62-
style: ({progress}) => ({width: progress.toFixed(4) + '%'})
62+
style: ({ progress }) => ({ width: progress.toFixed(4) + '%' })
6363
})`
64-
background-color: #FFF;
64+
background-color: #fff;
6565
height: 3px;
6666
transition-property: width, background-color;
6767
transition-duration: 0.1s;
68-
transition-timing-function: cubic-bezier(1,0,.7,1);
68+
transition-timing-function: cubic-bezier(1, 0, 0.7, 1);
6969
`
7070

7171
const ProgressBar = styled.div`
@@ -80,8 +80,9 @@ const Details = styled.div`
8080
margin-left: 20px;
8181
width: 150px;
8282
height: 40px;
83-
letter-spacing: .9px;
84-
h5, h3 {
83+
letter-spacing: 0.9px;
84+
h5,
85+
h3 {
8586
margin: 0;
8687
}
8788
h5 {
@@ -93,30 +94,43 @@ const Details = styled.div`
9394
}
9495
`
9596

96-
const Player = ({playback, currentTime, duration, resumeTrack, pauseTrack}) => {
97+
const Player = ({
98+
playback,
99+
currentTime,
100+
duration,
101+
resumeTrack,
102+
pauseTrack,
103+
nextTrack,
104+
previousTrack
105+
}) => {
97106
const { currentTrack } = playback.queue
98-
const currentTimeString = new Date(currentTime * 1000).toISOString().substr(11, 8)
99-
const durationString = new Date((duration || 0) * 1000).toISOString().substr(11, 8)
107+
const currentTimeString = new Date(currentTime * 1000)
108+
.toISOString()
109+
.substr(11, 8)
110+
const durationString = new Date((duration || 0) * 1000)
111+
.toISOString()
112+
.substr(11, 8)
100113
return (
101114
<StyleBase>
102115
<div>
103-
<div className="control">
104-
<div className='player-prev' />
116+
<div className='control'>
117+
<div className='player-prev' onClick={previousTrack} />
105118
<PlayerControl
106119
onClick={
107120
playback.player
108-
? playback.player.paused
109-
? resumeTrack
110-
: pauseTrack
111-
: undefined
121+
? playback.player.paused ? resumeTrack : pauseTrack
122+
: undefined
112123
}
113-
isPlaying={playback.player && !playback.player.paused} />
114-
<div className='player-next' />
124+
isPlaying={playback.player && !playback.player.paused}
125+
/>
126+
<div className='player-next' onClick={nextTrack} />
115127
</div>
116128
<Progress>
117129
<span className='current'>{currentTimeString}</span>
118-
<ProgressBar >
119-
<ProgressStatus progress={duration > 0 ? (currentTime / duration ) * 100 : 0} />
130+
<ProgressBar>
131+
<ProgressStatus
132+
progress={duration > 0 ? currentTime / duration * 100 : 0}
133+
/>
120134
</ProgressBar>
121135
<span className='ends'>{durationString}</span>
122136
</Progress>

src/Player/index.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import React from 'react'
22
import * as R from 'ramda'
33
import { connect } from 'react-redux'
44
import { bindActionCreators } from 'redux'
5-
import { pauseTrack, resumeTrack } from '../Player/actions'
5+
import {
6+
pauseTrack,
7+
resumeTrack,
8+
nextTrack,
9+
previousTrack
10+
} from '../Player/actions'
611

712
import Player from './components'
813

@@ -28,16 +33,22 @@ class PlayerContainer extends React.Component {
2833
return (
2934
<Player
3035
{...this.state}
36+
nextTrack={this.props.nextTrack}
37+
previousTrack={this.props.previousTrack}
3138
pauseTrack={this.props.pauseTrack}
3239
resumeTrack={this.props.resumeTrack}
33-
playback={this.props.playback} />
40+
playback={this.props.playback}
41+
/>
3442
)
3543
}
3644
}
37-
const mapDispatchToProps = (dispatch) => {
38-
return bindActionCreators({ pauseTrack, resumeTrack }, dispatch)
45+
const mapDispatchToProps = dispatch => {
46+
return bindActionCreators(
47+
{ pauseTrack, resumeTrack, nextTrack, previousTrack },
48+
dispatch
49+
)
3950
}
40-
const mapStateToProps = (state) => {
51+
const mapStateToProps = state => {
4152
return R.pick(['playback'], state)
4253
}
4354

src/Player/reducer.js

+71-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const initialState = {
66
pausedAtTime: 0,
77
queue: {
88
currentTrack: null,
9+
currentPlayList: [],
910
nextTracks: [],
1011
queuedTracks: []
1112
}
@@ -14,7 +15,10 @@ const initialState = {
1415
export default (state = initialState, action) => {
1516
switch (action.type) {
1617
case t.START_TRACK:
17-
const surah = action.payload
18+
const { payload : {surah, playlist}} = action
19+
const surahIndex = R.findIndex(R.equals(surah))(playlist)
20+
const currentPlayList = playlist
21+
const nextTracks = R.slice(R.inc(surahIndex), Infinity, playlist)
1822
const player = new Audio(surah.url)
1923
// TODO: Uncaught (in promise) DOMException: The play() request was
2024
// interrupted by a call to pause(). https://goo.gl/LdLk22
@@ -27,7 +31,9 @@ export default (state = initialState, action) => {
2731
player,
2832
queue: {
2933
...state.queue,
30-
currentTrack: surah
34+
currentTrack: surah,
35+
nextTracks,
36+
currentPlayList
3137
}
3238
}
3339

@@ -41,6 +47,7 @@ export default (state = initialState, action) => {
4147
}
4248

4349
case t.RESUME_TRACK:
50+
// eslint-disable-next-line
4451
state.player
4552
state.player.currentTime = state.pausedAtTime
4653
state.player.play()
@@ -79,6 +86,68 @@ export default (state = initialState, action) => {
7986
]
8087
}
8188
}
89+
90+
case t.NEXT_TRACK:
91+
if (!state.player) {
92+
return state
93+
} else if (state.queue.queuedTracks.length > 0) {
94+
const currentTrack = state.queue.queuedTracks[0]
95+
const queuedTracks = R.slice(1, Infinity, state.queue.queuedTracks)
96+
const player = new Audio(currentTrack.url)
97+
player.load()
98+
!!state.player && state.player.pause()
99+
const playPromise = player.play()
100+
playPromise.catch()
101+
return {
102+
...state,
103+
player,
104+
queue: {
105+
...state.queue,
106+
currentTrack,
107+
queuedTracks
108+
}
109+
}
110+
} else {
111+
const currentTrack = state.queue.nextTracks[0]
112+
const nextTracks = R.slice(1, Infinity, state.queue.nextTracks)
113+
const player = new Audio(currentTrack.url)
114+
player.load()
115+
!!state.player && state.player.pause()
116+
const playPromise = player.play()
117+
playPromise.catch()
118+
return {
119+
...state,
120+
player,
121+
queue: {
122+
...state.queue,
123+
currentTrack,
124+
nextTracks
125+
}
126+
}
127+
}
128+
129+
case t.PREVIOUS_TRACK:
130+
if (!state.player) {
131+
return state
132+
}
133+
const currentTrackIndex = R.findIndex(R.equals(state.queue.currentTrack))(state.queue.currentPlayList)
134+
const currentTrack = state.queue.currentPlayList[R.dec(currentTrackIndex)]
135+
const newNextTracks = R.prepend(state.queue.currentTrack, state.queue.nextTracks)
136+
137+
const previousTrackPlayer = new Audio(currentTrack.url)
138+
previousTrackPlayer.load()
139+
!!state.player && state.player.pause()
140+
previousTrackPlayer.play().catch()
141+
return {
142+
...state,
143+
player: previousTrackPlayer,
144+
queue: {
145+
...state.queue,
146+
currentTrack,
147+
nextTracks: newNextTracks
148+
}
149+
}
150+
82151
default:
83152
return state
84153
}

0 commit comments

Comments
 (0)