Skip to content

Commit

Permalink
Episodio 3 - parte 1
Browse files Browse the repository at this point in the history
  • Loading branch information
durancristhian committed Aug 29, 2020
1 parent 59af753 commit 6775dce
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 13 deletions.
18 changes: 10 additions & 8 deletions components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,18 @@ const Layout = ({ children }: Props) => {
export default Layout

function LoggedIn() {
const { googleSignIn } = useAuth()

return (
<>
<Link href="/signin" passHref>
<A href="#!">Sign in</A>
</Link>
<div className="ml-4">
<Link href="/signup" passHref>
<A href="#!">Sign up</A>
</Link>
</div>
<button
onClick={() => {
googleSignIn()
}}
className="text-blue-700 focus:outline-none focus:shadow-outline"
>
Google Login
</button>
</>
)
}
Expand Down
15 changes: 15 additions & 0 deletions hooks/useAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ type ContextData = {
signin: (email: string, password: string) => Promise<firebase.User | null>
signup: (email: string, password: string) => Promise<firebase.User | null>
signout: () => Promise<void>
googleSignIn: () => Promise<firebase.User | null>
}

const defaultContextData = {
user: null,
signin: () => Promise.resolve(null),
signup: () => Promise.resolve(null),
signout: () => Promise.resolve(void 0),
googleSignIn: () => Promise.resolve(null),
}

const AuthContext = createContext<ContextData>(defaultContextData)
Expand Down Expand Up @@ -70,6 +72,18 @@ function useProvideAuth() {
})
}

const googleSignIn = () => {
const provider = new fuego.auth.GoogleAuthProvider()

fuego
.auth()
.signInWithPopup(provider)
.then((response) => {
setUser(response.user)
return response.user
})
}

const signout = () => {
return fuego
.auth()
Expand All @@ -90,6 +104,7 @@ function useProvideAuth() {
return {
user,
signin,
googleSignIn,
signup,
signout,
}
Expand Down
189 changes: 184 additions & 5 deletions pages/games/[gameId].tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { useDocument } from '@nandorojo/swr-firestore'
import { fuego, useDocument, useCollection } from '@nandorojo/swr-firestore'
import { useRouter } from 'next/router'
import React from 'react'
import React, { useState } from 'react'
import { Game } from '../../types/Game'
import Heading from '../../ui/Heading'
import { useAuth } from '../../hooks/useAuth'
import Button from '../../ui/Button'

const GameId = () => {
const router = useRouter()
const gameId = router.query.gameId
const { data, error, loading } = useDocument<Game>(
const { data: game, error, loading, update } = useDocument<Game>(
gameId ? `challenges/${gameId}` : null,
{
parseDates: ['createdAt'],
listen: true,
},
)
const { user } = useAuth()

if (loading) {
return <p className="italic text-center">Loading...</p>
Expand All @@ -22,15 +26,190 @@ const GameId = () => {
return <p className="italic text-center">There was an error.</p>
}

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

const isAdmin = game.createdBy === user?.uid

const nextState = () => {
const nextStatus =
game.status === 'created'
? 'playing'
: game.status === 'playing'
? 'finished'
: 'created'

update({
status: nextStatus,
})
}

return (
<>
<Heading>{data.name}</Heading>
<div className="bg-white flex p-4 shadow">
<div className="w-32">
<img src={game.cover} alt={game.name} className="shadow" />
</div>
<div className="flex-auto pl-4">
<div className="mb-4">
<Heading align="left">{game.name}</Heading>
</div>
<div className="mb-4">
<p className="italic">{game.description}</p>
</div>
<div className="mb-4">
<p>{game.createdAt.toLocaleString()}</p>
</div>
<p>{game.questions.length} questions</p>
</div>
</div>
{isAdmin && game.status !== 'finished' && (
<div className="mt-4 text-center">
<button
type="submit"
className="border bg-blue-200 border-blue-500 px-4 py-2 disabled:opacity-50 focus:outline-none focus:shadow-outline"
onClick={nextState}
>
{game.status === 'created'
? 'Play'
: game.status === 'playing'
? 'Finish'
: ''}
</button>
</div>
)}
<div className="mt-4">
{game.status === 'created' && <Created></Created>}
{game.status === 'playing' && <Playing gameId={gameId}></Playing>}
{game.status === 'finished' && <Finished gameId={gameId}></Finished>}
</div>
</>
)
}

export default GameId

function Created() {
return (
<>
<p className="italic text-center">
This game is not ready to play yet...
</p>
</>
)
}

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()

if (!user) {
return (
<p className="italic text-center">You have to log in before playing.</p>
)
}

const play = () => {
if (!user.displayName || !user.email) {
return alert('You need a display name and email')
}

add({
name: user.displayName,
email: user.email,
score: Math.round(Math.random() * 100),
})
}

return (
<>
<Heading>Playing</Heading>
<div className="mt-4 text-center">
<Button type="submit" onClick={play}>
Ready to play
</Button>
</div>
</>
)
}

type FinishedProps = {
gameId: string
}

function Finished({ gameId }: FinishedProps) {
const [isVisible, setVisibility] = useState(false)

const seePodium = () => {
setVisibility(true)
}

return (
<>
<Heading>Stats</Heading>
{!isVisible && (
<div className="mt-4 text-center">
<Button onClick={seePodium}>See podium</Button>
</div>
)}
{isVisible && <Table gameId={gameId} />}
</>
)
}

type TableProps = {
gameId: string
}

function Table({ gameId }: TableProps) {
const { data: players, error, loading } = useCollection<Player>(
`challenges/${gameId}/players`,
{
orderBy: ['score', 'desc'],
},
)

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

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

if (!players || !players.length) {
return <p className="italic text-center">There is no players.</p>
}

return (
<table className="table-fixed w-full">
<thead>
<tr className="bg-white text-left">
<th className="border w-1/3 px-4 py-2">Position</th>
<th className="border w-1/3 px-4 py-2">Name</th>
<th className="border w-1/3 px-4 py-2">Score</th>
</tr>
</thead>
<tbody>
{players.map((player, i) => (
<tr key={player.id}>
<td className="border px-4 py-2">{i + 1}</td>
<td className="border px-4 py-2">{player.name}</td>
<td className="border px-4 py-2">{player.score}</td>
</tr>
))}
</tbody>
</table>
)
}
18 changes: 18 additions & 0 deletions ui/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { ButtonHTMLAttributes, ReactNode } from 'react'

type Props = ButtonHTMLAttributes<HTMLButtonElement> & {
children: ReactNode
}

const Button = ({ children, ...props }: Props) => {
return (
<button
className="border bg-blue-200 border-blue-500 px-4 py-2 disabled:opacity-50 focus:outline-none focus:shadow-outline"
{...props}
>
{children}
</button>
)
}

export default Button

0 comments on commit 6775dce

Please sign in to comment.