Timer.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import { useState, useEffect, useRef } from 'react'
  2. import { TTraining } from '../../training/types'
  3. import { getExerciseList, getTrainingTime, getPosition } from '../utils'
  4. import Countdown from './Countdown'
  5. import AudioPlayer from './AudioPlayer'
  6. import { IExerciseItem } from '../types'
  7. import { useTimer } from '../hooks/useTimer'
  8. import { useVoice } from '../hooks/useVoice'
  9. import { useVideo } from '../hooks/useVideo'
  10. import theme from '../../styles/theme'
  11. const Timer = ({ training }: { training: TTraining }) => {
  12. const [time, timer] = useTimer({ tickPeriod: 100 })
  13. const voice = useVoice('rosie')
  14. const [state, setState] = useState({
  15. exerciseList: [] as IExerciseItem[],
  16. totalTime: 0
  17. })
  18. useEffect(() => {
  19. voice.play('ttt')
  20. const exerciseList = getExerciseList(training.blocks)
  21. const totalTime = getTrainingTime(exerciseList)
  22. setState({ ...state, exerciseList, totalTime })
  23. }, [training])
  24. const {
  25. currentExercise,
  26. previousExercise,
  27. nextExercise,
  28. exerciseTime
  29. } = getPosition(state.exerciseList, timer.time)
  30. const videoSrc =
  31. (currentExercise && currentExercise.video) || '/media/block0.mp4'
  32. const [videoRef, videoPlayer] = useVideo(videoSrc)
  33. useEffect(() => {
  34. if (time > state.totalTime) stopTimer()
  35. const countdown = currentExercise
  36. ? currentExercise.duration + currentExercise.offset - timer.intTime + 1
  37. : 0
  38. if (timer.running && voice && !voice.playing()) {
  39. if (exerciseTime < 1) {
  40. if (currentExercise && currentExercise.exercise !== 'Rest')
  41. voice.play('go')
  42. else voice.play('rest')
  43. } else if (countdown === 90) voice.play('ninety')
  44. else if (countdown === 60) voice.play('sixty')
  45. else if (countdown === 30) voice.play('thirty')
  46. else if (countdown === 5) voice.play('five')
  47. else if (countdown === 4) voice.play('four')
  48. else if (countdown === 3) voice.play('three')
  49. else if (countdown === 2) voice.play('two')
  50. else if (countdown === 1) voice.play('one')
  51. }
  52. }, [time, timer.running])
  53. function startTimer() {
  54. if (time >= state.totalTime) return
  55. timer.start()
  56. if (videoPlayer) videoPlayer.play()
  57. //if (audio.current) audio.current.play();
  58. }
  59. function stopTimer() {
  60. timer.stop()
  61. if (videoPlayer) videoPlayer.pause()
  62. //if (audio.current) audio.current.pause();
  63. }
  64. function forward() {
  65. if (nextExercise) {
  66. timer.setTime(nextExercise.offset)
  67. }
  68. }
  69. function back() {
  70. if (previousExercise) {
  71. timer.setTime(previousExercise.offset)
  72. }
  73. }
  74. return (
  75. <p id='timer'>
  76. <h1>{(currentExercise && currentExercise.toplevelBlock) || 'Torture'}</h1>
  77. <div id='flow'>
  78. <Countdown
  79. seconds={
  80. currentExercise ? currentExercise.duration - exerciseTime : 0
  81. }
  82. totalPercent={timer.time / state.totalTime}
  83. exercisePercent={
  84. exerciseTime / (currentExercise ? currentExercise.duration : 1)
  85. }
  86. onClick={timer.running ? stopTimer : startTimer}
  87. />
  88. <div id='controls'>
  89. {/*<label htmlFor="rest">Rest</label>
  90. <input type="number" min="25" max="60" step="5" defaultValue="25" />*/}
  91. <button onClick={back} disabled={timer.running}>
  92. back
  93. </button>
  94. <button onClick={timer.running ? stopTimer : startTimer}>
  95. {timer.running ? 'stop' : 'start'}
  96. </button>
  97. <button onClick={forward} disabled={timer.running}>
  98. forward
  99. </button>
  100. </div>
  101. <h2>current exercise</h2>
  102. <p className='exercise'>
  103. {currentExercise ? currentExercise.exercise : '😎'}
  104. </p>
  105. <h2>next up</h2>
  106. <p className='exercise'>
  107. {nextExercise ? nextExercise.exercise : '😎'}
  108. </p>
  109. </div>
  110. <p id='description'>
  111. <div data-vjs-player>
  112. <video ref={videoRef} className='video-js vjs-16-9' />
  113. </div>
  114. <p className='description'>
  115. {currentExercise && currentExercise.description}
  116. </p>
  117. </p>
  118. <style jsx>{`
  119. #timer {
  120. text-align: center;
  121. }
  122. #timer h1 {
  123. margin: 0 auto 0.8rem auto;
  124. }
  125. #flow h2 {
  126. margin-top: 0.4em;
  127. font-size: 90%;
  128. font-weight: 300;
  129. text-transform: uppercase;
  130. margin: auto;
  131. }
  132. #flow p {
  133. font-size: 110%;
  134. font-weight: 900;
  135. margin: 0 auto 0.8em auto;
  136. }
  137. #controls {
  138. margin-bottom: 0.4rem;
  139. }
  140. button {
  141. margin: 0.3rem;
  142. padding: 0.3rem;
  143. min-width: 6rem;
  144. border: none;
  145. background-color: ${theme.colors.darkblue};
  146. color: ${theme.colors.offWhite};
  147. cursor: pointer;
  148. }
  149. @media (min-width: 768px) {
  150. #timer {
  151. display: grid;
  152. grid-template-columns: 1fr 1fr;
  153. }
  154. h1 {
  155. margin: 0 auto 1.4rem auto;
  156. grid-column: 1/3;
  157. }
  158. #flow h2 {
  159. margin-bottom: 0.6rem;
  160. }
  161. #controls,
  162. #flow p {
  163. margin-bottom: 1.4rem;
  164. }
  165. button {
  166. padding: 0.6rem;
  167. margin: 0.6rem;
  168. }
  169. .header,
  170. .exercise {
  171. grid-column: 1/2;
  172. }
  173. .description {
  174. grid-column: 2/3;
  175. }
  176. }
  177. `}</style>
  178. </p>
  179. )
  180. }
  181. export default Timer