Skip to content

Commit

Permalink
Episodio 5
Browse files Browse the repository at this point in the history
  • Loading branch information
durancristhian committed Sep 5, 2020
1 parent 4a42b79 commit 88c00cb
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 7 deletions.
43 changes: 43 additions & 0 deletions components/Countdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useState } from 'react'
import useInterval from '../hooks/useInterval'

type Props = {
time: number
onFinish: () => void
}

const Countdown = ({ time, onFinish }: Props) => {
const [translateLeft, setTranslateLeft] = useState(100)
const [finished, setFinished] = useState(false)
const step = 100 / time

useInterval(
() => {
let nextTranslateLeft = Math.round(translateLeft - step)

if (nextTranslateLeft < 0) {
nextTranslateLeft = 0

onFinish()
setFinished(true)
}

setTranslateLeft(nextTranslateLeft)
},
1000,
!finished,
)

return (
<div className="bg-white h-4 relative shadow overflow-hidden">
<div
className="absolute bg-red-500 left-0 bottom-0 top-0 w-full h-full transition-transform duration-1000 ease-linear"
style={{
transform: `translateX(-${translateLeft}%)`,
}}
></div>
</div>
)
}

export default Countdown
1 change: 1 addition & 0 deletions components/ListChallenges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const ListChallenges = () => {
listen: true,
parseDates: ['createdAt'],
where: ['createdBy', '==', user.uid],
orderBy: ['createdAt', 'desc'],
})

if (loading) {
Expand Down
32 changes: 32 additions & 0 deletions hooks/useAudio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useState } from 'react'

const useAudio = (url: string, volume = 0.5) => {
const sound = new Audio(url)
sound.volume = volume

const [audio] = useState(sound)
const [playing, setPlaying] = useState(false)

const toggle = () => setPlaying(!playing)

useEffect(() => {
if (playing) {
audio.play()
} else {
audio.currentTime = 0
audio.pause()
}
}, [playing])

useEffect(() => {
audio.addEventListener('ended', () => setPlaying(false))

return () => {
audio.removeEventListener('ended', () => setPlaying(false))
}
}, [])

return { playing, toggle }
}

export default useAudio
7 changes: 4 additions & 3 deletions hooks/useCremona.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ export default function useCremona(
successfulChoices: 0,
})

const update = (selectedOption: Option) => {
const update = (selectedOption: Option | null) => {
setState((prev) => {
const currentIndex = prev.currentIndex + 1
const isValid =
game.questions[prev.currentIndex].validOption === selectedOption.id
const isValid = selectedOption
? game.questions[prev.currentIndex].validOption === selectedOption.id
: false
const successfulChoices = isValid
? prev.successfulChoices + 1
: prev.successfulChoices
Expand Down
27 changes: 27 additions & 0 deletions hooks/useInterval.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useEffect, useRef } from 'react'

export default function useInterval(
callback: () => void,
delay: number,
inRunning: boolean,
) {
const savedCallback = useRef<() => void>()

useEffect(() => {
savedCallback.current = callback
})

useEffect(() => {
function tick() {
if (savedCallback.current) {
savedCallback.current()
}
}

if (inRunning) {
const id = setInterval(tick, delay)

return () => clearInterval(id)
}
}, [inRunning])
}
61 changes: 57 additions & 4 deletions pages/games/[gameId]/[playerId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { Game, Option } from '../../../types/Game'
import { Player } from '../../../types/Player'
import Button from '../../../ui/Button'
import useCremona from '../../../hooks/useCremona'
import useAudio from '../../../hooks/useAudio'
import useInterval from '../../../hooks/useInterval'
import Countdown from '../../../components/Countdown'
import Link from 'next/link'
import A from '../../../ui/A'

const PlayerId = () => {
const router = useRouter()
Expand Down Expand Up @@ -53,7 +58,16 @@ const PlayerId = () => {
/* TODO: verify game and player status */
if (player.status === 'finished') {
/* return <PlayerScore /> */
return <p>You finished.</p>
return (
<>
<p>You finished with {player.score} points.</p>
<div className="mt-4">
<Link href="/games/[gameId]" as={`/games/${gameId}`} passHref>
<A href="#!">Go back</A>
</Link>
</div>
</>
)
}

const onFinish = (score: number) => {
Expand Down Expand Up @@ -101,6 +115,23 @@ function PlayerGame({ game, onFinish }: GameProps) {
successfulChoices,
} = useCremona(game, onFinish)
const [selectedOption, setSelectedOption] = useState<Option | null>(null)
const { toggle: playError } = useAudio('/sounds/error.mp3', 0.2)
const { toggle: playSuccess } = useAudio('/sounds/success.mp3', 0.9)
const { playing, toggle: toggleBackground } = useAudio(
'/sounds/background.mp3',
0.1,
)
const [showNext, setShowNext] = useState(false)

useInterval(
() => {
if (!playing) {
toggleBackground()
}
},
100,
!showNext,
)

const currentQuestion = game.questions[currentIndex]

Expand All @@ -119,15 +150,24 @@ function PlayerGame({ game, onFinish }: GameProps) {
<button
className={classnames([
'block px-4 py-2 my-4 bg-white border w-full text-left',
selectedOption &&
showNext &&
currentQuestion.validOption === option.id &&
'bg-green-300',
selectedOption &&
showNext &&
currentQuestion.validOption !== option.id &&
'bg-red-300',
])}
onClick={() => {
setSelectedOption(option)
setShowNext(true)

toggleBackground()

if (currentQuestion.validOption === option.id) {
playSuccess()
} else {
playError()
}
}}
disabled={!!selectedOption}
>
Expand All @@ -140,16 +180,29 @@ function PlayerGame({ game, onFinish }: GameProps) {
<p className="mt-4 text-center italic">
Question {currentIndex + 1} of {totalQuestions}
</p>
{selectedOption && (
{showNext && (
<Button
onClick={() => {
update(selectedOption)
setSelectedOption(null)
setShowNext(false)
}}
>
{currentIndex === totalQuestions - 1 ? 'Finish' : 'Next'}
</Button>
)}
{!showNext && (
<div className="mt-4">
<Countdown
time={currentQuestion.time}
onFinish={() => {
playError()
setSelectedOption(null)
setShowNext(true)
}}
/>
</div>
)}
</div>
)
}
Binary file added public/sounds/background.mp3
Binary file not shown.
Binary file added public/sounds/error.mp3
Binary file not shown.
Binary file added public/sounds/success.mp3
Binary file not shown.

1 comment on commit 88c00cb

@vercel
Copy link

@vercel vercel bot commented on 88c00cb Sep 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.