Skip to content

Commit

Permalink
Merge branch 'chore/remove-cloud-quiz-folder' into chore/rename-direc…
Browse files Browse the repository at this point in the history
…tory
  • Loading branch information
LukeSchlangen committed Aug 22, 2023
2 parents 49a0706 + 82a9733 commit a0f9f65
Show file tree
Hide file tree
Showing 55 changed files with 5,847 additions and 1,161 deletions.
32 changes: 32 additions & 0 deletions app-dev/cloud-quiz/app/(authenticated-pages)/create-game/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

"use client"

import CreateGameForm from "@/app/components/create-game-form";
import Link from "next/link";

export default function Home() {
return (
<div>
<Link href="/">
<button className={`border mt-5 p-2`}>← Exit</button>
</Link>
<br />
<CreateGameForm />
</div>
)
}
30 changes: 30 additions & 0 deletions app-dev/cloud-quiz/app/(authenticated-pages)/game-list/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

"use client"

import GameList from "@/app/components/game-list";
import Navbar from "@/app/components/navbar";

export default function Home() {
return (
<div>
<Navbar />
<GameList />
<br />
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

"use client"

import { useEffect } from 'react';
import useFirebaseAuthentication from "@/app/hooks/use-firebase-authentication";
import { gameStates } from "@/app/types";
import PlayerLobby from "@/app/components/player-lobby";
import QuestionPanel from "@/app/components/question-panel";
import useGame from "@/app/hooks/use-game";
import ReturnToHomepagePanel from '@/app/components/return-to-homepage-panel';

export default function GamePage() {
const authUser = useFirebaseAuthentication();
const { game, gameId, gameRef, isShowingQuestion, currentQuestion, error: errorMessage } = useGame();

useEffect(() => {
if (authUser.uid && Object.keys(game.players)) {
const joinGame = async () => {
if (!Object.keys(game.players).includes(authUser.uid)) {
const token = await authUser.getIdToken();
await fetch('/api/join-game', {
method: 'POST',
body: JSON.stringify({ gameId }),
headers: {
Authorization: token,
}
}).catch(error => {
console.error({ error })
});
}
}

joinGame();
}
}, [authUser, authUser.uid, game.players, gameId])

if (errorMessage) {
return (
<ReturnToHomepagePanel>
<h2>{errorMessage}</h2>
</ReturnToHomepagePanel>
)
}

return (
<>
{(game.state === gameStates.GAME_OVER) && (
<ReturnToHomepagePanel>
<h2>Game Over</h2>
</ReturnToHomepagePanel>
)}
{isShowingQuestion && (<QuestionPanel game={game} gameRef={gameRef} currentQuestion={currentQuestion} />)}
{game.state === gameStates.NOT_STARTED && (<PlayerLobby game={game} gameRef={gameRef} />)}
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

"use client"

import { useEffect } from 'react';
import useFirebaseAuthentication from "@/app/hooks/use-firebase-authentication";
import { gameStates } from "@/app/types";
import PresenterLobby from "@/app/components/presenter-lobby";
import QuestionPanel from "@/app/components/question-panel";
import useGame from "@/app/hooks/use-game";
import ReturnToHomepagePanel from '@/app/components/return-to-homepage-panel';

export default function GamePage() {
const authUser = useFirebaseAuthentication();
const { game, gameId, gameRef, isShowingQuestion, currentQuestion, error: errorMessage } = useGame();

useEffect(() => {
if (authUser.uid) {
const exitGame = async () => {
if (Object.keys(game.players).includes(authUser.uid)) {
const token = await authUser.getIdToken();
await fetch('/api/exit-game', {
method: 'POST',
body: JSON.stringify({ gameId }),
headers: {
Authorization: token,
}
}).catch(error => {
console.error({ error })
});
}
}

exitGame();
}
}, [authUser, authUser.uid, game.players, gameId])

if (errorMessage) {
return (
<ReturnToHomepagePanel>
<h2>{errorMessage}</h2>
</ReturnToHomepagePanel>
)
}

return (
<>
{(game.state === gameStates.GAME_OVER) && (
<ReturnToHomepagePanel>
<h2>Game Over</h2>
</ReturnToHomepagePanel>
)}
{isShowingQuestion && gameRef && (<>
<QuestionPanel game={game} gameRef={gameRef} currentQuestion={currentQuestion} />
</>)}
{game.state === gameStates.NOT_STARTED && gameRef && (<>
<PresenterLobby game={game} gameRef={gameRef} />
</>)}
</>
)
}
40 changes: 40 additions & 0 deletions app-dev/cloud-quiz/app/(authenticated-pages)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

"use client"

import useActiveGameList from "@/app/hooks/use-active-game-list";
import { useRouter } from 'next/navigation'
import Navbar from "@/app/components/navbar";

export default function Home() {
const { activeGameList } = useActiveGameList();
const router = useRouter()

if (activeGameList.length > 0) {
const firstGameId = activeGameList[0].id;
router.push(`/game/${firstGameId}`);
}

return (
<div>
<Navbar />
<center className="p-8">
Waiting for a game.
</center>
</div>
)
}
23 changes: 23 additions & 0 deletions app-dev/cloud-quiz/app/(unauthenticated-pages)/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export default function About() {
return (
<>
<a href="https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/app-dev/party-game">GitHub Repository</a>
</>
)
}
38 changes: 38 additions & 0 deletions app-dev/cloud-quiz/app/(unauthenticated-pages)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Navbar from '@/app/components/navbar'
import '@/app/globals.css'

export const metadata = {
title: 'About Party Game',
description: 'An open-source party game to learn about Google Cloud.',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
<Navbar />
<main className="mt-5 mx-auto max-w-2xl underline hover:decoration-[var(--google-cloud-blue)]">
{children}
</main>
</>
)
}
78 changes: 78 additions & 0 deletions app-dev/cloud-quiz/app/api/create-game/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { gamesRef, questionsRef } from '@/app/lib/firebase-server-initialization';
import { gameFormValidator } from '@/app/lib/game-form-validator';
import { generateName } from '@/app/lib/name-generator';
import { getAuthenticatedUser } from '@/app/lib/server-side-auth'
import { Question, gameStates } from '@/app/types';
import { QueryDocumentSnapshot, Timestamp } from 'firebase-admin/firestore';
import { NextRequest, NextResponse } from 'next/server'

export async function POST(request: NextRequest) {
let authUser;

try {
authUser = await getAuthenticatedUser(request);
} catch (error) {
console.error({ error });
// Respond with JSON indicating an error message
return new NextResponse(
JSON.stringify({ success: false, message: 'authentication failed' }),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
}

// Validate request
const { timePerQuestion, timePerAnswer }: { timePerQuestion: number, timePerAnswer: number } = await request.json();

const errorMessage = gameFormValidator({timePerQuestion, timePerAnswer});

if (errorMessage) {
// Respond with JSON indicating an error message
return new NextResponse(
JSON.stringify({ success: false, message: errorMessage }),
{ status: 400, headers: { 'content-type': 'application/json' } }
)
}

const querySnapshot = await questionsRef.get();
const questions = querySnapshot.docs.reduce((agg: Question[], doc: QueryDocumentSnapshot, index: number) => {
return { ...agg, [index]: doc.data() as Question }
}, {});
if (!authUser) throw new Error('User must be signed in to start game');
// create game with server endpoint

const leader = {
displayName: generateName(),
uid: authUser.uid,
};

const startTime = Timestamp.now();

const gameRef = await gamesRef.add({
questions,
leader,
players: {},
state: gameStates.NOT_STARTED,
currentQuestionIndex: 0,
startTime,
timePerQuestion,
timePerAnswer,
});

return NextResponse.json({ gameId: gameRef.id }, { status: 200 })
}
Loading

0 comments on commit a0f9f65

Please sign in to comment.