Skip to content

Commit

Permalink
Episodio 4
Browse files Browse the repository at this point in the history
  • Loading branch information
durancristhian committed Sep 1, 2020
1 parent 393c6d0 commit 6082985
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 13 deletions.
48 changes: 48 additions & 0 deletions components/CreateChallenge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,54 @@ const CreateChallenge = () => {
},
],
},
{
id: 'question-2',
description: 'Pregunta 2',
time: 30,
validOption: 'option-2',
options: [
{
id: 'option-1',
content: 'Cremona',
},
{
id: 'option-2',
content: 'Cañoncito de dulce de leche',
},
{
id: 'option-3',
content: 'Pepas',
},
{
id: 'option-4',
content: 'Sanguchitos de miga',
},
],
},
{
id: 'question-3',
description: 'Pregunta 3',
time: 30,
validOption: 'option-3',
options: [
{
id: 'option-1',
content: 'Cremona',
},
{
id: 'option-2',
content: 'Cañoncito de dulce de leche',
},
{
id: 'option-3',
content: 'Pepas',
},
{
id: 'option-4',
content: 'Sanguchitos de miga',
},
],
},
],
})

Expand Down
40 changes: 40 additions & 0 deletions hooks/useCremona.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useState } from 'react'
import { Game, Option } from '../types/Game'

export default function useCremona(
game: Game,
onFinish: (score: number) => void,
) {
const [state, setState] = useState({
currentIndex: 0,
successfulChoices: 0,
})

const update = (selectedOption: Option) => {
setState((prev) => {
const currentIndex = prev.currentIndex + 1
const isValid =
game.questions[prev.currentIndex].validOption === selectedOption.id
const successfulChoices = isValid
? prev.successfulChoices + 1
: prev.successfulChoices

if (game.questions.length <= currentIndex) {
onFinish(successfulChoices * 10)
}

return {
...prev,
currentIndex,
successfulChoices,
}
})
}

return {
...state,
totalQuestions: game.questions.length,
gameEnded: game.questions.length <= state.currentIndex,
update,
}
}
47 changes: 34 additions & 13 deletions pages/games/[gameId].tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { useCollection, useDocument } from '@nandorojo/swr-firestore'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React, { useState } from 'react'
import { isArray } from 'util'
import { useAuth } from '../../hooks/useAuth'
import { Game } from '../../types/Game'
import { Player } from '../../types/Player'
import A from '../../ui/A'
import Button from '../../ui/Button'
import Heading from '../../ui/Heading'

const GameId = () => {
const router = useRouter()
const gameId = isArray(router.query.gameId)
const gameId = Array.isArray(router.query.gameId)
? router.query.gameId[0]
: router.query.gameId
const { data: game, error, loading, update } = useDocument<Game>(
Expand Down Expand Up @@ -109,15 +111,15 @@ type PlayingProps = {
gameId: string
}

type Player = {
name: string
email: string
score: number
}

function Playing({ gameId }: PlayingProps) {
const { add } = useCollection<Player>(`challenges/${gameId}/players`)
const { user } = useAuth()
const { add, data: players } = useCollection<Player>(
user ? `challenges/${gameId}/players` : null,
{
where: ['email', '==', user?.email],
listen: true,
},
)

if (!user) {
return (
Expand All @@ -133,17 +135,36 @@ function Playing({ gameId }: PlayingProps) {
add({
name: user.displayName,
email: user.email,
score: Math.round(Math.random() * 100),
score: 0,
status: 'created',
})
}

const currentPlayer = players?.[0]

return (
<>
<Heading>Playing</Heading>
<div className="mt-4 text-center">
<Button type="submit" onClick={play}>
Ready to play
</Button>
{currentPlayer ? (
<div className="flex">
<div className="flex-auto px-4 text-left">
<Link
href="/games/[gameId]/[playerId]"
as={`/games/${gameId}/${currentPlayer.id}`}
passHref
>
<A href="#!">{currentPlayer.name}</A>
</Link>
</div>
<div className="px-4">{currentPlayer.status}</div>
<div className="px-4">{currentPlayer.score}</div>
</div>
) : (
<Button type="submit" onClick={play}>
Ready to play
</Button>
)}
</div>
</>
)
Expand Down
155 changes: 155 additions & 0 deletions pages/games/[gameId]/[playerId].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import classnames from 'classnames'
import React, { useState } from 'react'
import Heading from '../../../ui/Heading'
import { useRouter } from 'next/router'
import { useDocument } from '@nandorojo/swr-firestore'
import { Game, Option } from '../../../types/Game'
import { Player } from '../../../types/Player'
import Button from '../../../ui/Button'
import useCremona from '../../../hooks/useCremona'

const PlayerId = () => {
const router = useRouter()
const gameId = Array.isArray(router.query.gameId)
? router.query.gameId[0]
: router.query.gameId
const playerId = Array.isArray(router.query.playerId)
? router.query.playerId[0]
: router.query.playerId

const { data: game, error, loading } = useDocument<Game>(
gameId ? `challenges/${gameId}` : null,
{
parseDates: ['createdAt'],
listen: true,
},
)
const {
data: player,
error: errorPlayer,
loading: loadingPlayer,
update,
} = useDocument<Player>(
playerId ? `challenges/${gameId}/players/${playerId}` : null,
{
listen: true,
},
)

if (loading || loadingPlayer) {
return <p className="italic text-center">Loading...</p>
}

if (error || errorPlayer) {
return <p className="italic text-center">There was an error.</p>
}

if (!game || !game.id || !player) {
return (
<p className="italic text-center">There is no challenge or player.</p>
)
}

/* TODO: verify game and player status */
if (player.status === 'finished') {
/* return <PlayerScore /> */
return <p>You finished.</p>
}

const onFinish = (score: number) => {
console.log(score)

update({
status: 'finished',
score,
})
}

return (
<>
<Heading type="h1">{game.name}</Heading>
{player.status === 'playing' ? (
<PlayerGame game={game} onFinish={onFinish} />
) : (
<Button
onClick={() => {
update({
status: 'playing',
})
}}
>
Play
</Button>
)}
</>
)
}

export default PlayerId

type GameProps = {
game: Game
onFinish: (score: number) => void
}

function PlayerGame({ game, onFinish }: GameProps) {
const {
currentIndex,
totalQuestions,
update,
gameEnded,
successfulChoices,
} = useCremona(game, onFinish)
const [selectedOption, setSelectedOption] = useState<Option | null>(null)

const currentQuestion = game.questions[currentIndex]

if (gameEnded) {
return <Heading type="h2">You got {successfulChoices * 10} points.</Heading>
}

return (
<div className="">
<div className="mb-4">
<Heading type="h2">{currentQuestion.description}</Heading>
</div>
<ul className="flex flex-wrap">
{currentQuestion.options.map((option) => (
<li key={option.id} className="w-1/2">
<button
className={classnames([
'block px-4 py-2 my-4 bg-white border w-full text-left',
selectedOption &&
currentQuestion.validOption === option.id &&
'bg-green-300',
selectedOption &&
currentQuestion.validOption !== option.id &&
'bg-red-300',
])}
onClick={() => {
setSelectedOption(option)
}}
disabled={!!selectedOption}
>
{selectedOption?.id === option.id && '😎 '}
{option.content}
</button>
</li>
))}
</ul>
<p className="mt-4 text-center italic">
Question {currentIndex + 1} of {totalQuestions}
</p>
{selectedOption && (
<Button
onClick={() => {
update(selectedOption)
setSelectedOption(null)
}}
>
{currentIndex === totalQuestions - 1 ? 'Finish' : 'Next'}
</Button>
)}
</div>
)
}
6 changes: 6 additions & 0 deletions types/Player.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type Player = {
name: string
email: string
score: number
status: 'created' | 'playing' | 'finished'
}

1 comment on commit 6082985

@vercel
Copy link

@vercel vercel bot commented on 6082985 Sep 1, 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.