diff --git a/src/App.js b/src/App.js
index 12592c5..4367219 100644
--- a/src/App.js
+++ b/src/App.js
@@ -4,10 +4,12 @@ import { TextInput } from './components/TextInput'
import { TextOutput } from './components/TextOutput'
import { RsvpReader } from './components/RsvpReader'
+import { IconContext } from 'react-icons'
+
export function App() {
return (
-
+
-
+
)
}
diff --git a/src/components/Player.js b/src/components/Player.js
index f63326d..762f1de 100644
--- a/src/components/Player.js
+++ b/src/components/Player.js
@@ -1,26 +1,74 @@
-import React, { useEffect, useState, useCallback } from 'react'
+import React, { useState, useEffect, useRef } from 'react'
-export const PlayerControl = ({ interval, onTick, onStart, onStop }) => {
- const dispatch = useDispatch()
- const [isPlaying, setPlaying] = useState(false)
+import { FaPlay, FaPause, FaStop } from 'react-icons/fa'
- const [handle, setHandle] = useState(null)
+function safeCall(fn) {
+ typeof fn === 'function' && fn()
+}
- useEffect(() => {
- console.log('effect fired', isPlaying)
- clearInterval(handle)
- if (isPlaying) {
- setHandle(setInterval(() => dispatch(incrementSegment()), 500))
- }
- }, [handle, isPlaying])
+export const Player = ({ onTick, onStart, onStop, onPause, delay }) => {
+ const [isRunning, start, stop] = usePlayer(stop => onTick(stop), delay)
+
+ const handleStart = () => {
+ safeCall(onStart)
+ start()
+ }
+
+ const handleStop = () => {
+ safeCall(onStop)
+ stop()
+ }
+
+ const handlePause = () => {
+ safeCall(onPause)
+ stop()
+ }
return (
-
-
-
-
playing: {isPlaying.toString()}
-
interval: {handle}
+
+ {isRunning && (
+
+ )}{' '}
+ {!isRunning && (
+
+ )}
)
}
+
+function usePlayer(onTick, delay) {
+ const [isRunning, setRunning] = useState(false)
+ const start = () => setRunning(true)
+ const stop = () => setRunning(false)
+
+ useInterval(() => onTick(stop), isRunning ? delay : null)
+ return [isRunning, start, stop]
+}
+
+// from: https://overreacted.io/making-setinterval-declarative-with-react-hooks/
+function useInterval(callback, delay) {
+ const savedCallback = useRef()
+
+ // Remember the latest callback.
+ useEffect(() => {
+ savedCallback.current = callback
+ }, [callback])
+
+ // Set up the interval.
+ useEffect(() => {
+ function tick() {
+ savedCallback.current()
+ }
+ if (delay !== null) {
+ let id = setInterval(tick, delay)
+ return () => clearInterval(id)
+ }
+ }, [delay])
+}
diff --git a/src/components/PlayerControl.js b/src/components/PlayerControl.js
deleted file mode 100644
index 31ac7ad..0000000
--- a/src/components/PlayerControl.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import React, { useEffect, useState, useCallback } from 'react'
-
-import { useSelector, useDispatch } from 'react-redux'
-import { selectPlaying, hasNextSegment } from '../store/selectors'
-import { start, stop, pause, incrementSegment } from '../store/actions'
-
-export const PlayerControl = ({ onTick }) => {
- const dispatch = useDispatch()
- const isPlaying = useSelector(selectPlaying)
- const hasNext = useSelector(hasNextSegment)
- const wpm = useSelector(state => state.wpm)
-
- const [handle, setHandle] = useState(null)
-
- useEffect(() => {
- clearInterval(handle)
- if (isPlaying) {
- setHandle(setInterval(() => dispatch(incrementSegment()), 60000 / wpm))
- }
- }, [isPlaying, wpm])
-
- useEffect(() => {
- if (!hasNext) dispatch(pause())
- }, [hasNext, dispatch])
-
- return (
-
-
- {isPlaying && (
-
- )}{' '}
- {!isPlaying && (
-
- )}
-
playing: {isPlaying.toString()}
-
interval: {handle}
-
has next: {hasNext.toString()}
-
- )
-}
diff --git a/src/components/RsvpPlayer.js b/src/components/RsvpPlayer.js
new file mode 100644
index 0000000..7171080
--- /dev/null
+++ b/src/components/RsvpPlayer.js
@@ -0,0 +1,27 @@
+import React from 'react'
+
+import { useSelector, useDispatch } from 'react-redux'
+import { hasNextSegment } from '../store/selectors'
+import { incrementSegment, resetSegment } from '../store/actions'
+import { Player } from './Player'
+
+export const RsvpPlayer = () => {
+ const dispatch = useDispatch()
+ const hasNext = useSelector(hasNextSegment)
+ const wpm = useSelector(state => state.wpm)
+
+ const onTick = stop => {
+ if (!hasNext) return stop()
+ dispatch(incrementSegment())
+ }
+
+ return (
+
+
dispatch(resetSegment())}
+ />
+
+ )
+}
diff --git a/src/components/RsvpReader.js b/src/components/RsvpReader.js
index 42095eb..76977af 100644
--- a/src/components/RsvpReader.js
+++ b/src/components/RsvpReader.js
@@ -5,7 +5,7 @@ import { TextOutput } from './TextOutput'
import { MainControl } from './MainControl'
import { RsvpSegment } from './RsvpSegment'
import { RsvpOptions } from './RsvpOptions'
-import { PlayerControl } from './PlayerControl'
+import { RsvpPlayer } from './RsvpPlayer'
const FlexRow = styled.div`
display: flex;
@@ -26,7 +26,7 @@ export const RsvpReader = () => {
-
+
diff --git a/src/store/actions.js b/src/store/actions.js
index bc082be..f72dbfe 100644
--- a/src/store/actions.js
+++ b/src/store/actions.js
@@ -1,4 +1,5 @@
export const setText = text => ({ type: 'SET_TEXT', payload: text })
+export const resetSegment = () => ({ type: 'SET_CURRENT_SEGMENT', payload: 0 })
export const incrementSegment = () => ({ type: 'INC_SEGMENT' })
export const decrementSegment = () => ({ type: 'DEC_SEGMENT' })
export const incrementWord = () => ({ type: 'INC_WORD' })
@@ -10,6 +11,3 @@ export const setMaxLength = length => ({
payload: length
})
export const setWpm = wpm => ({ type: 'SET_WPM', payload: wpm })
-export const start = () => ({ type: 'START' })
-export const pause = () => ({ type: 'PAUSE' })
-export const stop = () => ({ type: 'STOP' })
diff --git a/src/store/reducer.js b/src/store/reducer.js
index 0cdabad..bdd471c 100644
--- a/src/store/reducer.js
+++ b/src/store/reducer.js
@@ -27,10 +27,7 @@ const reducer = {
INC_SENTENCE: state => ({ ...state, curIdx: selectNextSentence(state) }),
DEC_SENTENCE: state => ({ ...state, curIdx: selectPrevSentence(state) }),
SET_MAX_LENGTH: (state, payload) => ({ ...state, maxLength: payload }),
- SET_WPM: (state, payload) => ({ ...state, wpm: payload }),
- START: state => ({ ...state, isPlaying: true }),
- PAUSE: state => ({ ...state, isPlaying: false }),
- STOP: state => ({ ...state, isPlaying: false, curIdx: 0 })
+ SET_WPM: (state, payload) => ({ ...state, wpm: payload })
}
export const reducerFn = (state, { type, payload }) => {
diff --git a/src/store/selectors.js b/src/store/selectors.js
index fc5aef3..85aaa7f 100644
--- a/src/store/selectors.js
+++ b/src/store/selectors.js
@@ -77,7 +77,3 @@ export const selectPrevSentence = createSelector(
export const selectHasNextSentence = createSelector(selectNextSentence, Boolean)
export const selectHasPrevSentence = createSelector(selectPrevSentence, Boolean)
-
-// player
-
-export const selectPlaying = state => state.isPlaying